diff --git a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js index 1488a8aa3de6c..b9099022423f2 100644 --- a/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js +++ b/packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js @@ -624,6 +624,44 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => { expect(container.textContent).toEqual('NaN'); }); + test('selector is not called when snaphot has not changed', async () => { + const store = createExternalStore({a: 0}); + + function selector(state) { + Scheduler.log('Selector'); + return state.a; + } + + function isEqual(a, b) { + return a === b; + } + + function App() { + Scheduler.log('App'); + const a = useSyncExternalStoreWithSelector( + store.subscribe, + store.getState, + null, + selector, + isEqual, + ); + return ; + } + + const container = document.createElement('div'); + const root = createRoot(container); + await act(() => root.render()); + assertLog(['App', 'Selector', 'A0']); + + await act(() => store.set(store.getState())); + await act(() => store.set({a: 0})); + assertLog(['Selector']); + + await act(() => store.set(store.getState())); + await act(() => store.set(store.getState())); + assertLog([]); + }); + describe('extra features implemented in user-space', () => { // The selector implementation uses the lazy ref initialization pattern // @gate !(enableUseRefAccessWarning && __DEV__) diff --git a/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js b/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js index 5b04ae892c875..1dbfd74f2c235 100644 --- a/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js +++ b/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js @@ -93,6 +93,7 @@ export function useSyncExternalStoreWithSelector( // to React that the selections are conceptually equal, and we can bail // out of rendering. if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) { + memoizedSnapshot = nextSnapshot; return prevSelection; }