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: 3fa62480e8bc89fc3c8593937be75290864d88d3
Pull Request resolved: #30663
  • Loading branch information
poteto committed Aug 12, 2024
1 parent 87d2972 commit 61324e3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 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 1 but size 2 was requested.',
);
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -1637,8 +1637,7 @@ describe('ReactFreshIntegration', () => {
}
});

// eslint-disable-next-line jest/no-disabled-tests
it.skip('resets useMemoCache cache slots', async () => {
it('resets useMemoCache cache slots', async () => {
if (__DEV__) {
await render(`
const useMemoCache = require('react/compiler-runtime').c;
Expand Down
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 61324e3

Please sign in to comment.