diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 7538bb9b3c217..f7525d9034cad 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -1593,13 +1593,14 @@ function mountSyncExternalStore( return nextSnapshot; } -function updateSyncExternalStore( +function updateSyncExternalStoreImpl( + hook: Hook, + prevSnapshot: T, subscribe: (() => void) => () => void, getSnapshot: () => T, getServerSnapshot?: () => T, ): T { const fiber = currentlyRenderingFiber; - const hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the // normal rules of React, and only works because store updates are // always synchronous. @@ -1615,7 +1616,6 @@ function updateSyncExternalStore( } } } - const prevSnapshot = (currentHook || hook).memoizedState; const snapshotChanged = !is(prevSnapshot, nextSnapshot); if (snapshotChanged) { hook.memoizedState = nextSnapshot; @@ -1666,6 +1666,44 @@ function updateSyncExternalStore( return nextSnapshot; } +function updateSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const hook = updateWorkInProgressHook(); + const prevSnapshot = hook.memoizedState; + return updateSyncExternalStoreImpl( + hook, + prevSnapshot, + subscribe, + getSnapshot, + getServerSnapshot, + ); +} + +function rerenderSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const hook = updateWorkInProgressHook(); + const prevSnapshot = + currentHook === null + ? // This is a rerender during a mount. + hook.memoizedState + : // This is a rerender during an update. + currentHook.memoizedState; + + return updateSyncExternalStoreImpl( + hook, + prevSnapshot, + subscribe, + getSnapshot, + getServerSnapshot, + ); +} + function pushStoreConsistencyCheck( fiber: Fiber, getSnapshot: () => T, @@ -2798,7 +2836,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, useMutableSource: updateMutableSource, - useSyncExternalStore: updateSyncExternalStore, + useSyncExternalStore: rerenderSyncExternalStore, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -3443,7 +3481,11 @@ if (__DEV__) { ): T { currentHookNameInDev = 'useSyncExternalStore'; updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); + return rerenderSyncExternalStore( + subscribe, + getSnapshot, + getServerSnapshot, + ); }, useId(): string { currentHookNameInDev = 'useId'; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index e98d684a78e6f..04b89ea176f5e 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -1593,13 +1593,14 @@ function mountSyncExternalStore( return nextSnapshot; } -function updateSyncExternalStore( +function updateSyncExternalStoreImpl( + hook: Hook, + prevSnapshot: T, subscribe: (() => void) => () => void, getSnapshot: () => T, getServerSnapshot?: () => T, ): T { const fiber = currentlyRenderingFiber; - const hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the // normal rules of React, and only works because store updates are // always synchronous. @@ -1615,7 +1616,6 @@ function updateSyncExternalStore( } } } - const prevSnapshot = (currentHook || hook).memoizedState; const snapshotChanged = !is(prevSnapshot, nextSnapshot); if (snapshotChanged) { hook.memoizedState = nextSnapshot; @@ -1666,6 +1666,44 @@ function updateSyncExternalStore( return nextSnapshot; } +function updateSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const hook = updateWorkInProgressHook(); + const prevSnapshot = hook.memoizedState; + return updateSyncExternalStoreImpl( + hook, + prevSnapshot, + subscribe, + getSnapshot, + getServerSnapshot, + ); +} + +function rerenderSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const hook = updateWorkInProgressHook(); + const prevSnapshot = + currentHook === null + ? // This is a rerender during a mount. + hook.memoizedState + : // This is a rerender during an update. + currentHook.memoizedState; + + return updateSyncExternalStoreImpl( + hook, + prevSnapshot, + subscribe, + getSnapshot, + getServerSnapshot, + ); +} + function pushStoreConsistencyCheck( fiber: Fiber, getSnapshot: () => T, @@ -2798,7 +2836,7 @@ const HooksDispatcherOnRerender: Dispatcher = { useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, useMutableSource: updateMutableSource, - useSyncExternalStore: updateSyncExternalStore, + useSyncExternalStore: rerenderSyncExternalStore, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -3443,7 +3481,11 @@ if (__DEV__) { ): T { currentHookNameInDev = 'useSyncExternalStore'; updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); + return rerenderSyncExternalStore( + subscribe, + getSnapshot, + getServerSnapshot, + ); }, useId(): string { currentHookNameInDev = 'useId';