Skip to content

Commit 7bdd390

Browse files
committed
Unify context implementations
Implements the new context API on top of the existing ReactStack that we already use for host context and legacy context. Now there is a single array that we push and pop from. This makes the interrupt path slightly slower, since when we reset the unit of work pointer, we have to iterate over the stack (like before) *and* switch on the type of work (not like before). On the other hand, this unifies all of the unwinding behavior in the UnwindWork module.
1 parent 010c4ed commit 7bdd390

File tree

7 files changed

+71
-81
lines changed

7 files changed

+71
-81
lines changed

packages/react-reconciler/src/ReactFiberContext.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export type LegacyContext = {
4848
processChildContext(fiber: Fiber, parentContext: Object): Object,
4949
pushContextProvider(workInProgress: Fiber): boolean,
5050
invalidateContextProvider(workInProgress: Fiber, didChange: boolean): void,
51-
resetContext(): void,
5251
findCurrentUnmaskedContext(fiber: Fiber): Object,
5352
};
5453

@@ -292,12 +291,6 @@ export default function(stack: Stack): LegacyContext {
292291
}
293292
}
294293

295-
function resetContext(): void {
296-
previousContext = emptyObject;
297-
contextStackCursor.current = emptyObject;
298-
didPerformWorkStackCursor.current = false;
299-
}
300-
301294
function findCurrentUnmaskedContext(fiber: Fiber): Object {
302295
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
303296
// makes sense elsewhere
@@ -336,7 +329,6 @@ export default function(stack: Stack): LegacyContext {
336329
processChildContext,
337330
pushContextProvider,
338331
invalidateContextProvider,
339-
resetContext,
340332
findCurrentUnmaskedContext,
341333
};
342334
}

packages/react-reconciler/src/ReactFiberHostContext.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
108108
pop(contextFiberStackCursor, fiber);
109109
}
110110

111-
function resetHostContainer() {
112-
contextStackCursor.current = NO_CONTEXT;
113-
rootInstanceStackCursor.current = NO_CONTEXT;
114-
}
115-
116111
return {
117112
getHostContext,
118113
getRootHostContainer,
119114
popHostContainer,
120115
popHostContext,
121116
pushHostContainer,
122117
pushHostContext,
123-
resetHostContainer,
124118
};
125119
}

packages/react-reconciler/src/ReactFiberNewContext.js

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@
99

1010
import type {Fiber} from './ReactFiber';
1111
import type {ReactContext} from 'shared/ReactTypes';
12+
import type {StackCursor, Stack} from './ReactFiberStack';
1213

1314
import warning from 'fbjs/lib/warning';
1415

1516
export type NewContext = {
1617
pushProvider(providerFiber: Fiber): void,
1718
popProvider(providerFiber: Fiber): void,
18-
resetProviderStack(): void,
1919
};
2020

21-
export default function() {
22-
let changedBitsStack: Array<any> = [];
23-
let currentValueStack: Array<any> = [];
24-
let stack: Array<Fiber> = [];
25-
let index = -1;
21+
export default function(stack: Stack) {
22+
const {createCursor, push, pop} = stack;
23+
24+
const providerCursor: StackCursor<Fiber | null> = createCursor(null);
25+
const valueCursor: StackCursor<mixed> = createCursor(null);
26+
const changedBitsCursor: StackCursor<number> = createCursor(0);
2627

2728
let rendererSigil;
2829
if (__DEV__) {
@@ -32,10 +33,11 @@ export default function() {
3233

3334
function pushProvider(providerFiber: Fiber): void {
3435
const context: ReactContext<any> = providerFiber.type.context;
35-
index += 1;
36-
changedBitsStack[index] = context._changedBits;
37-
currentValueStack[index] = context._currentValue;
38-
stack[index] = providerFiber;
36+
37+
push(changedBitsCursor, context._changedBits, providerFiber);
38+
push(valueCursor, context._currentValue, providerFiber);
39+
push(providerCursor, providerFiber, providerFiber);
40+
3941
context._currentValue = providerFiber.pendingProps.value;
4042
context._changedBits = providerFiber.stateNode;
4143

@@ -51,39 +53,20 @@ export default function() {
5153
}
5254

5355
function popProvider(providerFiber: Fiber): void {
54-
if (__DEV__) {
55-
warning(index > -1 && providerFiber === stack[index], 'Unexpected pop.');
56-
}
57-
const changedBits = changedBitsStack[index];
58-
const currentValue = currentValueStack[index];
59-
changedBitsStack[index] = null;
60-
currentValueStack[index] = null;
61-
stack[index] = null;
62-
index -= 1;
56+
const changedBits = changedBitsCursor.current;
57+
const currentValue = valueCursor.current;
58+
59+
pop(providerCursor, providerFiber);
60+
pop(valueCursor, providerFiber);
61+
pop(changedBitsCursor, providerFiber);
62+
6363
const context: ReactContext<any> = providerFiber.type.context;
6464
context._currentValue = currentValue;
6565
context._changedBits = changedBits;
6666
}
6767

68-
function resetProviderStack(): void {
69-
for (let i = index; i > -1; i--) {
70-
const providerFiber = stack[i];
71-
const context: ReactContext<any> = providerFiber.type.context;
72-
context._currentValue = context._defaultValue;
73-
context._changedBits = 0;
74-
changedBitsStack[i] = null;
75-
currentValueStack[i] = null;
76-
stack[i] = null;
77-
if (__DEV__) {
78-
context._currentRenderer = null;
79-
}
80-
}
81-
index = -1;
82-
}
83-
8468
return {
8569
pushProvider,
8670
popProvider,
87-
resetProviderStack,
8871
};
8972
}

packages/react-reconciler/src/ReactFiberScheduler.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -157,21 +157,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
157157
config: HostConfig<T, P, I, TI, HI, PI, C, CC, CX, PL>,
158158
) {
159159
const stack = ReactFiberStack();
160-
const {reset: resetStack} = stack;
161160
const hostContext = ReactFiberHostContext(config, stack);
162161
const legacyContext = ReactFiberLegacyContext(stack);
163-
const newContext = ReactFiberNewContext();
162+
const newContext = ReactFiberNewContext(stack);
164163
const {popHostContext, popHostContainer} = hostContext;
165164
const {
166165
popTopLevelContextObject: popTopLevelLegacyContextObject,
167166
popContextProvider: popLegacyContextProvider,
168-
resetContext: resetLegacyContext,
169167
} = legacyContext;
170-
const {popProvider, resetProviderStack} = newContext;
168+
const {popProvider} = newContext;
171169
const hydrationContext: HydrationContext<C, CX> = ReactFiberHydrationContext(
172170
config,
173171
);
174-
const {resetHostContainer} = hostContext;
175172
const {beginWork} = ReactFiberBeginWork(
176173
config,
177174
hostContext,
@@ -188,7 +185,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
188185
newContext,
189186
hydrationContext,
190187
);
191-
const {throwException, unwindWork} = ReactFiberUnwindWork(
188+
const {
189+
throwException,
190+
unwindWork,
191+
unwindInterruptedWork,
192+
} = ReactFiberUnwindWork(
192193
hostContext,
193194
legacyContext,
194195
newContext,
@@ -289,15 +290,14 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
289290
};
290291
}
291292

292-
function resetContextStack() {
293-
// Reset the stack
294-
resetStack();
295-
// Reset the cursors
296-
resetLegacyContext();
297-
resetHostContainer();
298-
299-
// TODO: Unify new context implementation with other stacks
300-
resetProviderStack();
293+
function resetStack() {
294+
if (nextUnitOfWork !== null) {
295+
let interruptedWork = nextUnitOfWork.return;
296+
while (interruptedWork !== null) {
297+
unwindInterruptedWork(interruptedWork);
298+
interruptedWork = interruptedWork.return;
299+
}
300+
}
301301

302302
if (__DEV__) {
303303
ReactStrictModeWarnings.discardPendingWarnings();
@@ -841,7 +841,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
841841
nextUnitOfWork === null
842842
) {
843843
// Reset the stack and start working from the root.
844-
resetContextStack();
844+
resetStack();
845845
nextRoot = root;
846846
nextRenderExpirationTime = expirationTime;
847847
nextUnitOfWork = createWorkInProgress(
@@ -1102,7 +1102,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
11021102
) {
11031103
// This is an interruption. (Used for performance tracking.)
11041104
interruptedBy = fiber;
1105-
resetContextStack();
1105+
resetStack();
11061106
}
11071107
if (nextRoot !== root || !isWorking) {
11081108
requestWork(root, expirationTime);

packages/react-reconciler/src/ReactFiberStack.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export type Stack = {
2020
isEmpty(): boolean,
2121
push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void,
2222
pop<T>(cursor: StackCursor<T>, fiber: Fiber): void,
23-
reset(): void,
2423
};
2524

2625
export default function(): Stack {
@@ -81,23 +80,10 @@ export default function(): Stack {
8180
cursor.current = value;
8281
}
8382

84-
function reset(): void {
85-
while (index > -1) {
86-
valueStack[index] = null;
87-
88-
if (__DEV__) {
89-
fiberStack[index] = null;
90-
}
91-
92-
index--;
93-
}
94-
}
95-
9683
return {
9784
createCursor,
9885
isEmpty,
9986
pop,
10087
push,
101-
reset,
10288
};
10389
}

packages/react-reconciler/src/ReactFiberUnwindWork.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,36 @@ export default function<C, CX>(
145145
return null;
146146
}
147147
}
148+
149+
function unwindInterruptedWork(interruptedWork: Fiber) {
150+
switch (interruptedWork.tag) {
151+
case ClassComponent: {
152+
popLegacyContextProvider(interruptedWork);
153+
break;
154+
}
155+
case HostRoot: {
156+
popHostContainer(interruptedWork);
157+
popTopLevelLegacyContextObject(interruptedWork);
158+
break;
159+
}
160+
case HostComponent: {
161+
popHostContext(interruptedWork);
162+
break;
163+
}
164+
case HostPortal:
165+
popHostContainer(interruptedWork);
166+
break;
167+
case ContextProvider:
168+
popProvider(interruptedWork);
169+
break;
170+
default:
171+
break;
172+
}
173+
}
174+
148175
return {
149176
throwException,
150177
unwindWork,
178+
unwindInterruptedWork,
151179
};
152180
}

packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.internal.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,13 @@ ${formatActions(actions)}
542542
['c', step(2)],
543543
['b', interrupt()],
544544
);
545+
546+
simulateMultipleRoots(
547+
['c', toggle(0)],
548+
['c', step(1)],
549+
['b', flush(7)],
550+
['c', toggle(0)],
551+
);
545552
});
546553

547554
it('generative tests', () => {

0 commit comments

Comments
 (0)