Skip to content

Commit 4268962

Browse files
drarmstrgaearon
authored andcommitted
Fix production-only updateSyncExternalStore() crash when doing setState in render (facebook#23150)
* Update ReactFiberHooks.new.js * Add regression test + replace-fork * Prettier Co-authored-by: Dan Abramov <dan.abramov@me.com>
1 parent 6fa197f commit 4268962

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

packages/react-reconciler/src/ReactFiberHooks.new.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2471,7 +2471,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
24712471
useDeferredValue: rerenderDeferredValue,
24722472
useTransition: rerenderTransition,
24732473
useMutableSource: updateMutableSource,
2474-
useSyncExternalStore: mountSyncExternalStore,
2474+
useSyncExternalStore: updateSyncExternalStore,
24752475
useId: updateId,
24762476

24772477
unstable_isNewReconciler: enableNewReconciler,

packages/react-reconciler/src/ReactFiberHooks.old.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2471,7 +2471,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
24712471
useDeferredValue: rerenderDeferredValue,
24722472
useTransition: rerenderTransition,
24732473
useMutableSource: updateMutableSource,
2474-
useSyncExternalStore: mountSyncExternalStore,
2474+
useSyncExternalStore: updateSyncExternalStore,
24752475
useId: updateId,
24762476

24772477
unstable_isNewReconciler: enableNewReconciler,

packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js

+27
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,33 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
745745
});
746746
});
747747

748+
test('regression test for #23150', async () => {
749+
const store = createExternalStore('Initial');
750+
751+
function App() {
752+
const text = useSyncExternalStore(store.subscribe, store.getState);
753+
const [derivedText, setDerivedText] = useState(text);
754+
useEffect(() => {}, []);
755+
if (derivedText !== text.toUpperCase()) {
756+
setDerivedText(text.toUpperCase());
757+
}
758+
return <Text text={derivedText} />;
759+
}
760+
761+
const container = document.createElement('div');
762+
const root = createRoot(container);
763+
await act(() => root.render(<App />));
764+
765+
expect(Scheduler).toHaveYielded(['INITIAL']);
766+
expect(container.textContent).toEqual('INITIAL');
767+
768+
await act(() => {
769+
store.set('Updated');
770+
});
771+
expect(Scheduler).toHaveYielded(['UPDATED']);
772+
expect(container.textContent).toEqual('UPDATED');
773+
});
774+
748775
// The selector implementation uses the lazy ref initialization pattern
749776
// @gate !(enableUseRefAccessWarning && __DEV__)
750777
test('compares selection to rendered selection even if selector changes', async () => {

0 commit comments

Comments
 (0)