@@ -16,7 +16,12 @@ import type {
1616 Usable ,
1717 Thenable ,
1818} from 'shared/ReactTypes' ;
19- import type { Fiber , Dispatcher , HookType } from './ReactInternalTypes' ;
19+ import type {
20+ Fiber ,
21+ Dispatcher ,
22+ HookType ,
23+ MemoCache ,
24+ } from './ReactInternalTypes' ;
2025import type { Lanes , Lane } from './ReactFiberLane.new' ;
2126import type { HookFlags } from './ReactHookEffectTags' ;
2227import type { FiberRoot } from './ReactInternalTypes' ;
@@ -177,6 +182,8 @@ type StoreConsistencyCheck<T> = {
177182export type FunctionComponentUpdateQueue = {
178183 lastEffect : Effect | null ,
179184 stores : Array < StoreConsistencyCheck < any >> | null ,
185+ // NOTE: optional, only set when enableUseMemoCacheHook is enabled
186+ memoCache ?: MemoCache | null ,
180187} ;
181188
182189type BasicStateAction < S > = ( S => S ) | S ;
@@ -710,10 +717,23 @@ function updateWorkInProgressHook(): Hook {
710717 return workInProgressHook ;
711718}
712719
713- function createFunctionComponentUpdateQueue ( ) : FunctionComponentUpdateQueue {
714- return {
715- lastEffect : null ,
716- stores : null ,
720+ // NOTE: defining two versions of this function to avoid size impact when this feature is disabled.
721+ // Previously this function was inlined, the additional `memoCache` property makes it not inlined.
722+ let createFunctionComponentUpdateQueue : ( ) => FunctionComponentUpdateQueue ;
723+ if ( enableUseMemoCacheHook ) {
724+ createFunctionComponentUpdateQueue = ( ) => {
725+ return {
726+ lastEffect : null ,
727+ stores : null ,
728+ memoCache : null ,
729+ } ;
730+ } ;
731+ } else {
732+ createFunctionComponentUpdateQueue = ( ) => {
733+ return {
734+ lastEffect : null ,
735+ stores : null ,
736+ } ;
717737 } ;
718738}
719739
@@ -787,7 +807,59 @@ function use<T>(usable: Usable<T>): T {
787807}
788808
789809function useMemoCache ( size : number ) : Array < any > {
790- throw new Error ( 'Not implemented.' ) ;
810+ let memoCache = null ;
811+ // Fast-path, load memo cache from wip fiber if already prepared
812+ let updateQueue : FunctionComponentUpdateQueue | null = ( currentlyRenderingFiber . updateQueue : any ) ;
813+ if ( updateQueue !== null ) {
814+ memoCache = updateQueue . memoCache ;
815+ }
816+ // Otherwise clone from the current fiber
817+ // TODO: not sure how to access the current fiber here other than going through
818+ // currentlyRenderingFiber.alternate
819+ if ( memoCache == null ) {
820+ const current : Fiber | null = currentlyRenderingFiber . alternate ;
821+ if ( current !== null ) {
822+ const currentUpdateQueue : FunctionComponentUpdateQueue | null = ( current . updateQueue : any ) ;
823+ if ( currentUpdateQueue !== null ) {
824+ const currentMemoCache : ?MemoCache = currentUpdateQueue . memoCache ;
825+ if ( currentMemoCache != null ) {
826+ memoCache = {
827+ data : currentMemoCache . data . map ( array => array . slice ( ) ) ,
828+ index : 0 ,
829+ } ;
830+ }
831+ }
832+ }
833+ }
834+ // Finally fall back to allocating a fresh instance of the cache
835+ if (memoCache == null) {
836+ memoCache = {
837+ data : [ ] ,
838+ index : 0 ,
839+ } ;
840+ }
841+ if (updateQueue === null) {
842+ updateQueue = createFunctionComponentUpdateQueue ( ) ;
843+ currentlyRenderingFiber . updateQueue = updateQueue ;
844+ }
845+ updateQueue.memoCache = memoCache;
846+
847+ let data = memoCache.data[memoCache.index];
848+ if (data === undefined) {
849+ data = memoCache . data [ memoCache . index ] = new Array ( size ) ;
850+ } else if (data.length !== size) {
851+ // TODO: consider warning or throwing here
852+ if ( __DEV__ ) {
853+ console . error (
854+ 'Expected a constant size argument for each invocation of useMemoCache. ' +
855+ 'The previous cache was allocated with size %s but size %s was requested.' ,
856+ data . length ,
857+ size ,
858+ ) ;
859+ }
860+ }
861+ memoCache . index ++ ;
862+ return data ;
791863}
792864
793865function basicStateReducer < S > (state: S, action: BasicStateAction< S > ): S {
0 commit comments