Skip to content

Commit

Permalink
[fresh] Reset useMemoCache when size changes
Browse files Browse the repository at this point in the history
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: 48c5b9c848a0b9ed01576ad357e067b72e8f887f
Pull Request resolved: #30663
  • Loading branch information
poteto committed Aug 12, 2024
1 parent b33b881 commit 87466b0
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 7 deletions.
17 changes: 12 additions & 5 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1232,13 +1232,20 @@ function useMemoCache(size: number): Array<any> {
data[i] = REACT_MEMO_CACHE_SENTINEL;
}
} else if (data.length !== size) {
// TODO: consider warning or throwing here
if (__DEV__) {
console.error(
// reset cache for FastRefresh
data = memoCache.data[memoCache.index] = new Array(size);
for (let i = 0; i < size; i++) {
data[i] = REACT_MEMO_CACHE_SENTINEL;
}
} else {
throw new Error(
'Expected a constant size argument for each invocation of useMemoCache. ' +
'The previous cache was allocated with size %s but size %s was requested.',
data.length,
size,
'The previous cache was allocated with size ' +
data.length +
' but size ' +
size +
' was requested.',
);
}
}
Expand Down
32 changes: 32 additions & 0 deletions packages/react-reconciler/src/__tests__/useMemoCache-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -621,4 +621,36 @@ describe('useMemoCache()', () => {
</>,
);
});

// @gate enableUseMemoCacheHook
it('throws when cache size varies between renders in non-dev', async () => {
function Component(props) {
const cache = useMemoCache(props.cacheSize);
expect(Array.isArray(cache)).toBe(true);
expect(cache.length).toBe(props.cacheSize);
expect(cache[0]).toBe(MemoCacheSentinel);
return cache.length;
}
const root = ReactNoop.createRoot();
await act(() => {
root.render(<Component key="comp" cacheSize={1} />);
});
expect(root).toMatchRenderedOutput('1');

if (__DEV__) {
await act(() => {
root.render(<Component key="comp" cacheSize={2} />);
});
expect(root).toMatchRenderedOutput('2');
} else {
await expect(async () => {
await act(() => {
root.render(<Component key="comp" cacheSize={2} />);
});
}).rejects.toThrow(
'Expected a constant size argument for each invocation of useMemoCache. ' +
'The previous cache was allocated with size %s but size %s was requested.',
);
}
});
});
5 changes: 3 additions & 2 deletions scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -526,5 +526,6 @@
"538": "Cannot use state or effect Hooks in renderToMarkup because this component will never be hydrated.",
"539": "Binary RSC chunks cannot be encoded as strings. This is a bug in the wiring of the React streams.",
"540": "String chunks need to be passed in their original shape. Not split into smaller string chunks. This is a bug in the wiring of the React streams.",
"541": "Compared context values must be arrays"
}
"541": "Compared context values must be arrays",
"542": "Expected a constant size argument for each invocation of useMemoCache. The previous cache was allocated with size %s but size %s was requested."
}

0 comments on commit 87466b0

Please sign in to comment.