Skip to content

Commit

Permalink
backport #3655
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Sep 2, 2022
1 parent 176bd3c commit 4ea4f93
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
9 changes: 7 additions & 2 deletions compat/src/index.js
Expand Up @@ -140,8 +140,13 @@ export const useInsertionEffect = useLayoutEffect;
export function useSyncExternalStore(subscribe, getSnapshot) {
const [state, setState] = useState(getSnapshot);

// TODO: in suspense for data we could have a discrepancy here because Preact won't re-init the "useState"
// when this unsuspends which could lead to stale state as the subscription is torn down.
const value = getSnapshot();

useLayoutEffect(() => {
if (value !== state) {
setState(() => value);
}
}, [subscribe, value, getSnapshot]);

useEffect(() => {
return subscribe(() => {
Expand Down
43 changes: 37 additions & 6 deletions compat/test/browser/hooks.test.js
Expand Up @@ -4,7 +4,9 @@ import React, {
useInsertionEffect,
useSyncExternalStore,
useTransition,
render
render,
useState,
useCallback
} from 'preact/compat';
import { setupRerender, act } from 'preact/test-utils';
import { setupScratch, teardown } from '../../../test/_util/helpers';
Expand Down Expand Up @@ -91,7 +93,7 @@ describe('React-18-hooks', () => {
});
expect(scratch.innerHTML).to.equal('<p>hello world</p>');
expect(subscribe).to.be.calledOnce;
expect(getSnapshot).to.be.calledOnce;
expect(getSnapshot).to.be.calledTwice;
});

it('subscribes and rerenders when called', () => {
Expand Down Expand Up @@ -119,7 +121,7 @@ describe('React-18-hooks', () => {
});
expect(scratch.innerHTML).to.equal('<p>hello world</p>');
expect(subscribe).to.be.calledOnce;
expect(getSnapshot).to.be.calledOnce;
expect(getSnapshot).to.be.calledTwice;

called = true;
flush();
Expand All @@ -135,9 +137,11 @@ describe('React-18-hooks', () => {
return () => {};
});

const func = () => 'value: ' + i++;

let i = 0;
const getSnapshot = sinon.spy(() => {
return () => 'value: ' + i++;
return func;
});

const App = () => {
Expand All @@ -150,12 +154,39 @@ describe('React-18-hooks', () => {
});
expect(scratch.innerHTML).to.equal('<p>value: 0</p>');
expect(subscribe).to.be.calledOnce;
expect(getSnapshot).to.be.calledOnce;
expect(getSnapshot).to.be.calledTwice;

flush();
rerender();

expect(scratch.innerHTML).to.equal('<p>value: 1</p>');
expect(scratch.innerHTML).to.equal('<p>value: 0</p>');
});

it('works with useCallback', () => {
let toggle;
const App = () => {
const [state, setState] = useState(true);
toggle = setState.bind(this, () => false);

const value = useSyncExternalStore(
useCallback(() => {
return () => {};
}, [state]),
() => (state ? 'yep' : 'nope')
);

return <p>{value}</p>;
};

act(() => {
render(<App />, scratch);
});
expect(scratch.innerHTML).to.equal('<p>yep</p>');

toggle();
rerender();

expect(scratch.innerHTML).to.equal('<p>nope</p>');
});
});
});

0 comments on commit 4ea4f93

Please sign in to comment.