-
Notifications
You must be signed in to change notification settings - Fork 51.1k
Expand file tree
/
Copy pathReactFiberCacheComponent.js
More file actions
126 lines (110 loc) · 3.42 KB
/
ReactFiberCacheComponent.js
File metadata and controls
126 lines (110 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {ReactContext} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import {pushProvider, popProvider} from './ReactFiberNewContext';
import * as Scheduler from 'scheduler';
// In environments without AbortController (e.g. tests)
// replace it with a lightweight shim that only has the features we use.
const AbortControllerLocal: typeof AbortController =
typeof AbortController !== 'undefined'
? AbortController
: // $FlowFixMe[incompatible-type]
// $FlowFixMe[missing-this-annot]
function AbortControllerShim() {
const listeners = [];
const signal = (this.signal = {
aborted: false as boolean,
addEventListener: (type, listener) => {
listeners.push(listener);
},
});
this.abort = () => {
signal.aborted = true;
listeners.forEach(listener => listener());
};
};
export type Cache = {
controller: AbortController,
data: Map<() => mixed, mixed>,
refCount: number,
};
export type CacheComponentState = {
+parent: Cache,
+cache: Cache,
};
export type SpawnedCachePool = {
+parent: Cache,
+pool: Cache,
};
// Intentionally not named imports because Rollup would
// use dynamic dispatch for CommonJS interop named imports.
const {
unstable_scheduleCallback: scheduleCallback,
unstable_NormalPriority: NormalPriority,
} = Scheduler;
export const CacheContext: ReactContext<Cache> = {
$$typeof: REACT_CONTEXT_TYPE,
// We don't use Consumer/Provider for Cache components. So we'll cheat.
Consumer: (null: any),
Provider: (null: any),
// We'll initialize these at the root.
_currentValue: (null: any),
_currentValue2: (null: any),
_threadCount: 0,
};
if (__DEV__) {
CacheContext._currentRenderer = null;
CacheContext._currentRenderer2 = null;
}
// Creates a new empty Cache instance with a ref-count of 0. The caller is responsible
// for retaining the cache once it is in use (retainCache), and releasing the cache
// once it is no longer needed (releaseCache).
export function createCache(): Cache {
return {
controller: new AbortControllerLocal(),
data: new Map(),
refCount: 0,
};
}
export function retainCache(cache: Cache) {
if (__DEV__) {
if (cache.controller.signal.aborted) {
console.warn(
'A cache instance was retained after it was already freed. ' +
'This likely indicates a bug in React.',
);
}
}
cache.refCount++;
}
// Cleanup a cache instance, potentially freeing it if there are no more references
export function releaseCache(cache: Cache) {
cache.refCount--;
if (__DEV__) {
if (cache.refCount < 0) {
console.warn(
'A cache instance was released after it was already freed. ' +
'This likely indicates a bug in React.',
);
}
}
if (cache.refCount === 0) {
scheduleCallback(NormalPriority, () => {
cache.controller.abort();
});
}
}
export function pushCacheProvider(workInProgress: Fiber, cache: Cache) {
pushProvider(workInProgress, CacheContext, cache);
}
export function popCacheProvider(workInProgress: Fiber, cache: Cache) {
popProvider(CacheContext, workInProgress);
}