mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix useSyncExternalStore dropped update when state is dispatched in render phase (#25578)
Fix https://github.com/facebook/react/issues/25565
This commit is contained in:
committed by
GitHub
parent
18dff7990a
commit
1e3e30dae2
@@ -1615,7 +1615,7 @@ function updateSyncExternalStore<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
const prevSnapshot = hook.memoizedState;
|
||||
const prevSnapshot = (currentHook || hook).memoizedState;
|
||||
const snapshotChanged = !is(prevSnapshot, nextSnapshot);
|
||||
if (snapshotChanged) {
|
||||
hook.memoizedState = nextSnapshot;
|
||||
|
||||
@@ -1615,7 +1615,7 @@ function updateSyncExternalStore<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
const prevSnapshot = hook.memoizedState;
|
||||
const prevSnapshot = (currentHook || hook).memoizedState;
|
||||
const snapshotChanged = !is(prevSnapshot, nextSnapshot);
|
||||
if (snapshotChanged) {
|
||||
hook.memoizedState = nextSnapshot;
|
||||
|
||||
@@ -18,6 +18,7 @@ let useLayoutEffect;
|
||||
let forwardRef;
|
||||
let useImperativeHandle;
|
||||
let useRef;
|
||||
let useState;
|
||||
let startTransition;
|
||||
|
||||
// This tests the native useSyncExternalStore implementation, not the shim.
|
||||
@@ -36,6 +37,7 @@ describe('useSyncExternalStore', () => {
|
||||
useImperativeHandle = React.useImperativeHandle;
|
||||
forwardRef = React.forwardRef;
|
||||
useRef = React.useRef;
|
||||
useState = React.useState;
|
||||
useSyncExternalStore = React.useSyncExternalStore;
|
||||
startTransition = React.startTransition;
|
||||
|
||||
@@ -173,4 +175,33 @@ describe('useSyncExternalStore', () => {
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test('next value is correctly cached when state is dispatched in render phase', async () => {
|
||||
const store = createExternalStore('value:initial');
|
||||
|
||||
function App() {
|
||||
const value = useSyncExternalStore(store.subscribe, store.getState);
|
||||
const [sameValue, setSameValue] = useState(value);
|
||||
if (value !== sameValue) setSameValue(value);
|
||||
return <Text text={value} />;
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
act(() => {
|
||||
// Start a render that reads from the store and yields value
|
||||
root.render(<App />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['value:initial']);
|
||||
|
||||
await act(() => {
|
||||
store.set('value:changed');
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['value:changed']);
|
||||
|
||||
// If cached value was updated, we expect a re-render
|
||||
await act(() => {
|
||||
store.set('value:initial');
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['value:initial']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user