Skip to content

Commit b8e67c3

Browse files
committed
[wip] partial passive effects stage cleanup of cache instances
1 parent 9031457 commit b8e67c3

6 files changed

+140
-8
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.new.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import type {
2323
OffscreenProps,
2424
OffscreenState,
2525
} from './ReactFiberOffscreenComponent';
26-
import type {
26+
import {
2727
Cache,
2828
CacheComponentState,
29+
releaseCache,
30+
retainCache,
2931
SpawnedCachePool,
3032
} from './ReactFiberCacheComponent.new';
3133
import type {UpdateQueue} from './ReactUpdateQueue.new';
@@ -75,6 +77,7 @@ import {
7577
StaticMask,
7678
ShouldCapture,
7779
ForceClientRender,
80+
Passive,
7881
} from './ReactFiberFlags';
7982
import ReactSharedInternals from 'shared/ReactSharedInternals';
8083
import {

packages/react-reconciler/src/ReactFiberCacheComponent.new.js

+34-4
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,55 @@ const prevFreshCacheOnStack: StackCursor<Cache | null> = createCursor(null);
6969
// * Call releaseCache() when any reference to the cache is "released" (ie
7070
// when the reference is no longer reachable). This *includes* the original
7171
// reference created w createCache().
72+
let _cacheIndex = 0;
7273
export function createCache(): Cache {
73-
return {
74+
const index = _cacheIndex++;
75+
const stack = new Error().stack
76+
.split('\n')
77+
.slice(1)
78+
.join('\n');
79+
const cache: Cache = {
7480
controller: new AbortController(),
7581
data: new Map(),
7682
refCount: 1,
7783
};
84+
(cache: any).stack = stack;
85+
(cache: any).key = String(index);
86+
// console.log(`createCache #${cache.key}:\n` + stack);
87+
return cache;
7888
}
7989

8090
export function retainCache(cache: Cache) {
91+
console.log(
92+
`retainCache #${cache.key} ${cache.refCount} -> ${cache.refCount + 1}:\n`,
93+
);
94+
// console.log(
95+
// `retainCache ${cache.refCount} -> ${cache.refCount + 1}:\n` +
96+
// new Error().stack
97+
// .split('\n')
98+
// .slice(1)
99+
// .join('\n'),
100+
// );
81101
cache.refCount++;
82102
}
83103

84104
export function releaseCache(cache: Cache) {
105+
console.log(
106+
`releaseCache #${cache.key} ${cache.refCount} -> ${cache.refCount - 1}:\n`,
107+
);
108+
// console.log(
109+
// `releaseCache ${cache.refCount} -> ${cache.refCount - 1}:\n` +
110+
// new Error().stack
111+
// .split('\n')
112+
// .slice(1)
113+
// .join('\n'),
114+
// );
85115
cache.refCount--;
86116
if (__DEV__) {
87117
if (cache.refCount < 0) {
88-
throw new Error(
89-
'Error in React: cache reference count should not be negative',
90-
);
118+
// throw new Error(
119+
// 'Error in React: cache reference count should not be negative',
120+
// );
91121
}
92122
}
93123
if (cache.refCount === 0) {

packages/react-reconciler/src/ReactFiberCommitWork.new.js

+62
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
2424
import type {Wakeable} from 'shared/ReactTypes';
2525
import type {OffscreenState} from './ReactFiberOffscreenComponent';
2626
import type {HookFlags} from './ReactHookEffectTags';
27+
import type {Cache} from './ReactFiberCacheComponent.new';
2728

2829
import {
2930
enableCreateEventHandleAPI,
@@ -38,6 +39,7 @@ import {
3839
enableSuspenseLayoutEffectSemantics,
3940
enableUpdaterTracking,
4041
warnAboutCallbackRefReturningFunction,
42+
enableCache,
4143
} from 'shared/ReactFeatureFlags';
4244
import {
4345
FunctionComponent,
@@ -57,6 +59,7 @@ import {
5759
ScopeComponent,
5860
OffscreenComponent,
5961
LegacyHiddenComponent,
62+
CacheComponent,
6063
} from './ReactWorkTags';
6164
import {detachDeletedInstance} from './ReactFiberHostConfig';
6265
import {
@@ -143,6 +146,7 @@ import {
143146
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';
144147
import {doesFiberContain} from './ReactFiberTreeReflection';
145148
import {invokeGuardedCallback, clearCaughtError} from 'shared/ReactErrorUtils';
149+
import {releaseCache, retainCache} from './ReactFiberCacheComponent.new';
146150

147151
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
148152
if (__DEV__) {
@@ -2594,6 +2598,8 @@ function commitPassiveMountEffects_complete(
25942598
function commitPassiveMountOnFiber(
25952599
finishedRoot: FiberRoot,
25962600
finishedWork: Fiber,
2601+
// maybe thread through previous fiber or cache from previous fiber
2602+
//
25972603
): void {
25982604
switch (finishedWork.tag) {
25992605
case FunctionComponent:
@@ -2615,6 +2621,46 @@ function commitPassiveMountOnFiber(
26152621
}
26162622
break;
26172623
}
2624+
case HostRoot: {
2625+
if (enableCache) {
2626+
// todo compare caches here
2627+
// if caches not same
2628+
// - retain next cache
2629+
// - if prev cache non-null: release (or move to unmount phase?)
2630+
// add comment that retain/release on child is technically not required
2631+
const previousCache: Cache =
2632+
finishedWork.alternate?.memoizedState.cache;
2633+
const nextCache: Cache = finishedWork.memoizedState.cache;
2634+
if (nextCache !== previousCache) {
2635+
console.log('retain/release HostRoot cache');
2636+
// retainCache(nextCache); root is already the owner, no need to retain
2637+
if (previousCache != null) {
2638+
releaseCache(previousCache);
2639+
}
2640+
}
2641+
}
2642+
break;
2643+
}
2644+
case CacheComponent: {
2645+
if (enableCache) {
2646+
// todo compare caches here
2647+
// if caches not same
2648+
// - retain next cache
2649+
// - if prev cache non-null: release (or move to unmount phase?)
2650+
// add comment that retain/release on child is technically not required
2651+
const previousCache: Cache =
2652+
finishedWork.alternate?.memoizedState.cache;
2653+
const nextCache: Cache = finishedWork.memoizedState.cache;
2654+
if (nextCache !== previousCache) {
2655+
console.log('retain/release CacheComponent cache');
2656+
retainCache(nextCache);
2657+
if (previousCache != null) {
2658+
releaseCache(previousCache);
2659+
}
2660+
}
2661+
}
2662+
break;
2663+
}
26182664
}
26192665
}
26202666

@@ -2821,6 +2867,22 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber(
28212867
}
28222868
break;
28232869
}
2870+
case HostRoot: {
2871+
if (enableCache) {
2872+
const cache = current.memoizedState.cache;
2873+
console.log('release HostRoot cache');
2874+
releaseCache(cache);
2875+
}
2876+
break;
2877+
}
2878+
case CacheComponent: {
2879+
if (enableCache) {
2880+
const cache = current.memoizedState.cache;
2881+
console.log('release CacheComponent cache');
2882+
releaseCache(cache);
2883+
}
2884+
break;
2885+
}
28242886
}
28252887
}
28262888

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import {
7272
ChildDeletion,
7373
StaticMask,
7474
MutationMask,
75+
Passive,
7576
} from './ReactFiberFlags';
7677
import invariant from 'shared/invariant';
7778

@@ -847,6 +848,7 @@ function completeWork(
847848
case HostRoot: {
848849
const fiberRoot = (workInProgress.stateNode: FiberRoot);
849850
if (enableCache) {
851+
workInProgress.flags |= Passive;
850852
popRootCachePool(fiberRoot, renderLanes);
851853

852854
const cache: Cache = workInProgress.memoizedState.cache;
@@ -1472,6 +1474,7 @@ function completeWork(
14721474
case CacheComponent: {
14731475
if (enableCache) {
14741476
const cache: Cache = workInProgress.memoizedState.cache;
1477+
workInProgress.flags |= Passive;
14751478
popCacheProvider(workInProgress, cache);
14761479
bubbleProperties(workInProgress);
14771480
return null;

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ import {
237237
isDevToolsPresent,
238238
} from './ReactFiberDevToolsHook.new';
239239
import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors';
240-
import {releaseCache} from './ReactFiberCacheComponent.new';
240+
import {releaseCache, retainCache} from './ReactFiberCacheComponent.new';
241241

242242
const ceil = Math.ceil;
243243

@@ -2115,7 +2115,7 @@ function releaseRootPooledCache(root) {
21152115
if (enableCache) {
21162116
const pooledCache = root.pooledCache;
21172117
if (pooledCache != null) {
2118-
releaseCache(pooledCache);
2118+
// releaseCache(pooledCache);
21192119
root.pooledCache = null;
21202120
}
21212121
}
@@ -2160,6 +2160,7 @@ export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {
21602160

21612161
function flushPassiveEffectsImpl() {
21622162
if (rootWithPendingPassiveEffects === null) {
2163+
console.log('flushPassiveEffectsImpl bailout');
21632164
return false;
21642165
}
21652166

packages/react-reconciler/src/__tests__/ReactCache-test.js

+34-1
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,43 @@ describe('ReactCache', () => {
161161
// @gate experimental || www
162162
test('render Cache component', async () => {
163163
const root = ReactNoop.createRoot();
164+
function Example(props) {
165+
// React.useEffect(() => {
166+
// console.log(
167+
// 'effect:\n' +
168+
// new Error().stack
169+
// .split('\n')
170+
// .slice(1)
171+
// .join('\n'),
172+
// );
173+
// return () => {
174+
// console.log(
175+
// 'cleanup:\n' +
176+
// new Error().stack
177+
// .split('\n')
178+
// .slice(1)
179+
// .join('\n'),
180+
// );
181+
// };
182+
// }, [props.text]);
183+
return <Cache>{props.text}</Cache>;
184+
}
185+
console.log('render: hi');
164186
await act(async () => {
165-
root.render(<Cache>Hi</Cache>);
187+
// root.render(<Cache>Hi</Cache>);
188+
root.render(<Example text="Hi" />);
166189
});
167190
expect(root).toMatchRenderedOutput('Hi');
191+
console.log('render: ...');
192+
await act(async () => {
193+
root.render(<Example key={2} text="..." />);
194+
});
195+
expect(root).toMatchRenderedOutput('...');
196+
console.log('render: bye');
197+
await act(async () => {
198+
root.render('Bye');
199+
});
200+
expect(root).toMatchRenderedOutput('Bye');
168201
});
169202

170203
// @gate experimental || www

0 commit comments

Comments
 (0)