Skip to content

Commit

Permalink
fix getSnapshot warning when a selector returns NaN (#23333)
Browse files Browse the repository at this point in the history
* fix getSnapshot warning when a selector returns NaN

useSyncExternalStoreWithSelector delegate a selector as
getSnapshot of useSyncExternalStore.

* Fiber's use sync external store has a same issue

* Small nits

We use Object.is to check whether the snapshot value has been updated,
so we should also use it to check whether the value is cached.

Co-authored-by: Andrew Clark <git@andrewclark.io>
  • Loading branch information
hachibeeDI and acdlite authored Feb 21, 2022
1 parent 40eaa22 commit 4de99b3
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 5 deletions.
6 changes: 4 additions & 2 deletions packages/react-reconciler/src/ReactFiberHooks.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,8 @@ function mountSyncExternalStore<T>(
nextSnapshot = getSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (nextSnapshot !== getSnapshot()) {
const cachedSnapshot = getSnapshot();
if (!is(nextSnapshot, cachedSnapshot)) {
console.error(
'The result of getSnapshot should be cached to avoid an infinite loop',
);
Expand Down Expand Up @@ -1362,7 +1363,8 @@ function updateSyncExternalStore<T>(
const nextSnapshot = getSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (nextSnapshot !== getSnapshot()) {
const cachedSnapshot = getSnapshot();
if (!is(nextSnapshot, cachedSnapshot)) {
console.error(
'The result of getSnapshot should be cached to avoid an infinite loop',
);
Expand Down
6 changes: 4 additions & 2 deletions packages/react-reconciler/src/ReactFiberHooks.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,8 @@ function mountSyncExternalStore<T>(
nextSnapshot = getSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (nextSnapshot !== getSnapshot()) {
const cachedSnapshot = getSnapshot();
if (!is(nextSnapshot, cachedSnapshot)) {
console.error(
'The result of getSnapshot should be cached to avoid an infinite loop',
);
Expand Down Expand Up @@ -1362,7 +1363,8 @@ function updateSyncExternalStore<T>(
const nextSnapshot = getSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (nextSnapshot !== getSnapshot()) {
const cachedSnapshot = getSnapshot();
if (!is(nextSnapshot, cachedSnapshot)) {
console.error(
'The result of getSnapshot should be cached to avoid an infinite loop',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,33 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
);
});

test('getSnapshot can return NaN without infinite loop warning', async () => {
const store = createExternalStore('not a number');

function App() {
const value = useSyncExternalStore(store.subscribe, () =>
parseInt(store.getState(), 10),
);
return <Text text={value} />;
}

const container = document.createElement('div');
const root = createRoot(container);

// Initial render that reads a snapshot of NaN. This is OK because we use
// Object.is algorithm to compare values.
await act(() => root.render(<App />));
expect(container.textContent).toEqual('NaN');

// Update to real number
await act(() => store.set(123));
expect(container.textContent).toEqual('123');

// Update back to NaN
await act(() => store.set('not a number'));
expect(container.textContent).toEqual('NaN');
});

describe('extra features implemented in user-space', () => {
// The selector implementation uses the lazy ref initialization pattern
// @gate !(enableUseRefAccessWarning && __DEV__)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export function useSyncExternalStore<T>(
const value = getSnapshot();
if (__DEV__) {
if (!didWarnUncachedGetSnapshot) {
if (value !== getSnapshot()) {
const cachedValue = getSnapshot();
if (!is(value, cachedValue)) {
console.error(
'The result of getSnapshot should be cached to avoid an infinite loop',
);
Expand Down

0 comments on commit 4de99b3

Please sign in to comment.