diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 3ba251b620713..87551125b8104 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -1232,13 +1232,20 @@ function useMemoCache(size: number): Array { 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.', ); } } diff --git a/packages/react-reconciler/src/__tests__/useMemoCache-test.js b/packages/react-reconciler/src/__tests__/useMemoCache-test.js index a3dbeeb38ce1c..fb4248cac6e14 100644 --- a/packages/react-reconciler/src/__tests__/useMemoCache-test.js +++ b/packages/react-reconciler/src/__tests__/useMemoCache-test.js @@ -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(); + }); + expect(root).toMatchRenderedOutput('1'); + + if (__DEV__) { + await act(() => { + root.render(); + }); + expect(root).toMatchRenderedOutput('2'); + } else { + await expect(async () => { + await act(() => { + root.render(); + }); + }).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.', + ); + } + }); }); diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index ba4fb4fa28428..af3d3296a4d45 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -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" -} \ No newline at end of file + "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." +}