From 69c7246d9dec4f5a235f3d8353811df3a80d24f5 Mon Sep 17 00:00:00 2001 From: Joseph Savona Date: Fri, 14 Oct 2022 15:00:31 -0700 Subject: [PATCH] Initialize useMemoCache with sentinel values (#25465) * Flush out useMemoCache API * rename symbol * rename symbol.for string name * workaround symbol export not working in unit tests --- .../react-reconciler/src/ReactFiberHooks.new.js | 6 ++++-- .../react-reconciler/src/ReactFiberHooks.old.js | 6 ++++-- .../src/__tests__/useMemoCache-test.js | 15 ++++++++++++++- packages/react-server/src/ReactFizzHooks.js | 7 ++++++- packages/react-server/src/ReactFlightHooks.js | 11 +++++++++-- packages/shared/ReactSymbols.js | 4 ++++ 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 2f854410afb11..110431001349f 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -46,6 +46,7 @@ import { import { REACT_CONTEXT_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_MEMO_CACHE_SENTINEL, } from 'shared/ReactSymbols'; import { @@ -845,8 +846,6 @@ function useMemoCache(size: number): Array { memoCache = updateQueue.memoCache; } // Otherwise clone from the current fiber - // TODO: not sure how to access the current fiber here other than going through - // currentlyRenderingFiber.alternate if (memoCache == null) { const current: Fiber | null = currentlyRenderingFiber.alternate; if (current !== null) { @@ -878,6 +877,9 @@ function useMemoCache(size: number): Array { let data = memoCache.data[memoCache.index]; if (data === undefined) { data = memoCache.data[memoCache.index] = new Array(size); + for (let i = 0; i < size; i++) { + data[i] = REACT_MEMO_CACHE_SENTINEL; + } } else if (data.length !== size) { // TODO: consider warning or throwing here if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index bbdb1e2478f99..527d442f88d22 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -46,6 +46,7 @@ import { import { REACT_CONTEXT_TYPE, REACT_SERVER_CONTEXT_TYPE, + REACT_MEMO_CACHE_SENTINEL, } from 'shared/ReactSymbols'; import { @@ -845,8 +846,6 @@ function useMemoCache(size: number): Array { memoCache = updateQueue.memoCache; } // Otherwise clone from the current fiber - // TODO: not sure how to access the current fiber here other than going through - // currentlyRenderingFiber.alternate if (memoCache == null) { const current: Fiber | null = currentlyRenderingFiber.alternate; if (current !== null) { @@ -878,6 +877,9 @@ function useMemoCache(size: number): Array { let data = memoCache.data[memoCache.index]; if (data === undefined) { data = memoCache.data[memoCache.index] = new Array(size); + for (let i = 0; i < size; i++) { + data[i] = REACT_MEMO_CACHE_SENTINEL; + } } else if (data.length !== size) { // TODO: consider warning or throwing here if (__DEV__) { diff --git a/packages/react-reconciler/src/__tests__/useMemoCache-test.js b/packages/react-reconciler/src/__tests__/useMemoCache-test.js index 94684ec8c00b3..bde6671fc5ac5 100644 --- a/packages/react-reconciler/src/__tests__/useMemoCache-test.js +++ b/packages/react-reconciler/src/__tests__/useMemoCache-test.js @@ -1,8 +1,19 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + * @jest-environment node + */ + let React; let ReactNoop; let act; let useState; let useMemoCache; +let MemoCacheSentinel; let ErrorBoundary; describe('useMemoCache()', () => { @@ -14,6 +25,7 @@ describe('useMemoCache()', () => { act = require('jest-react').act; useState = React.useState; useMemoCache = React.unstable_useMemoCache; + MemoCacheSentinel = Symbol.for('react.memo_cache_sentinel'); class _ErrorBoundary extends React.Component { constructor(props) { @@ -46,7 +58,8 @@ describe('useMemoCache()', () => { const cache = useMemoCache(1); expect(Array.isArray(cache)).toBe(true); expect(cache.length).toBe(1); - expect(cache[0]).toBe(undefined); + expect(cache[0]).toBe(MemoCacheSentinel); + return 'Ok'; } const root = ReactNoop.createRoot(); diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index bd0be42d14563..5d5eca52319de 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -46,6 +46,7 @@ import is from 'shared/objectIs'; import { REACT_SERVER_CONTEXT_TYPE, REACT_CONTEXT_TYPE, + REACT_MEMO_CACHE_SENTINEL, } from 'shared/ReactSymbols'; type BasicStateAction = (S => S) | S; @@ -666,7 +667,11 @@ function useCacheRefresh(): (?() => T, ?T) => void { } function useMemoCache(size: number): Array { - return new Array(size); + const data = new Array(size); + for (let i = 0; i < size; i++) { + data[i] = REACT_MEMO_CACHE_SENTINEL; + } + return data; } function noop(): void {} diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index a25a5f4b162ae..f4ee8d384d38b 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -11,7 +11,10 @@ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type {Request} from './ReactFlightServer'; import type {ReactServerContext, Thenable, Usable} from 'shared/ReactTypes'; import type {ThenableState} from './ReactFlightWakeable'; -import {REACT_SERVER_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import { + REACT_SERVER_CONTEXT_TYPE, + REACT_MEMO_CACHE_SENTINEL, +} from 'shared/ReactSymbols'; import {readContext as readContextImpl} from './ReactFlightNewContext'; import {enableUseHook} from 'shared/ReactFeatureFlags'; import { @@ -90,7 +93,11 @@ export const HooksDispatcher: Dispatcher = { return unsupportedRefresh; }, useMemoCache(size: number): Array { - return new Array(size); + const data = new Array(size); + for (let i = 0; i < size; i++) { + data[i] = REACT_MEMO_CACHE_SENTINEL; + } + return data; }, use: enableUseHook ? use : (unsupportedHook: any), }; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index c68e50d5c693e..74bb4f0de92d9 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -45,6 +45,10 @@ export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED: symbol = Symbol.for( 'react.default_value', ); +export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for( + 'react.memo_cache_sentinel', +); + const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator';