mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #8611 from bvaughn/shared-context-stack
Added ReactFiberStack shared by ReactFiberContext and ReactFiberHostContext
This commit is contained in:
@@ -270,14 +270,19 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
const root = (workInProgress.stateNode : FiberRoot);
|
||||
if (root.pendingContext) {
|
||||
pushTopLevelContextObject(
|
||||
workInProgress,
|
||||
root.pendingContext,
|
||||
root.pendingContext !== root.context
|
||||
);
|
||||
} else {
|
||||
pushTopLevelContextObject(root.context, false);
|
||||
pushTopLevelContextObject(
|
||||
workInProgress,
|
||||
root.context,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
pushHostContainer(root.containerInfo);
|
||||
pushHostContainer(workInProgress, root.containerInfo);
|
||||
|
||||
const updateQueue = workInProgress.updateQueue;
|
||||
if (updateQueue) {
|
||||
@@ -444,7 +449,7 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
}
|
||||
|
||||
function updatePortalComponent(current, workInProgress) {
|
||||
pushHostContainer(workInProgress.stateNode.containerInfo);
|
||||
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
||||
const priorityLevel = workInProgress.pendingWorkPriority;
|
||||
let nextChildren = workInProgress.pendingProps;
|
||||
if (hasContextChanged()) {
|
||||
@@ -535,7 +540,7 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
}
|
||||
break;
|
||||
case HostPortal:
|
||||
pushHostContainer(workInProgress.stateNode.containerInfo);
|
||||
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
||||
break;
|
||||
}
|
||||
// TODO: What if this is currently in progress?
|
||||
|
||||
@@ -176,7 +176,7 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
case ClassComponent: {
|
||||
// We are leaving this subtree, so pop context if any.
|
||||
if (isContextProvider(workInProgress)) {
|
||||
popContextProvider();
|
||||
popContextProvider(workInProgress);
|
||||
}
|
||||
// Don't use the state queue to compute the memoized state. We already
|
||||
// merged it and assigned it to the instance. Transfer it from there.
|
||||
@@ -303,7 +303,7 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
// TODO: Only mark this as an update if we have any pending callbacks.
|
||||
markUpdate(workInProgress);
|
||||
workInProgress.memoizedProps = workInProgress.pendingProps;
|
||||
popHostContainer();
|
||||
popHostContainer(workInProgress);
|
||||
return null;
|
||||
|
||||
// Error cases
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
'use strict';
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { StackCursor } from 'ReactFiberStack';
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var invariant = require('invariant');
|
||||
@@ -24,20 +25,21 @@ var {
|
||||
ClassComponent,
|
||||
HostRoot,
|
||||
} = require('ReactTypeOfWork');
|
||||
const {
|
||||
createCursor,
|
||||
pop,
|
||||
push,
|
||||
} = require('ReactFiberStack');
|
||||
|
||||
if (__DEV__) {
|
||||
var checkReactTypeSpec = require('checkReactTypeSpec');
|
||||
}
|
||||
|
||||
let index = -1;
|
||||
const contextStack : Array<Object> = [];
|
||||
const didPerformWorkStack : Array<boolean> = [];
|
||||
let contextStackCursor : StackCursor<?Object> = createCursor((null: ?Object));
|
||||
let didPerformWorkStackCursor : StackCursor<boolean> = createCursor(false);
|
||||
|
||||
function getUnmaskedContext() {
|
||||
if (index === -1) {
|
||||
return emptyObject;
|
||||
}
|
||||
return contextStack[index];
|
||||
return contextStackCursor.current || emptyObject;
|
||||
}
|
||||
|
||||
exports.getMaskedContext = function(workInProgress : Fiber) {
|
||||
@@ -49,6 +51,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) {
|
||||
|
||||
const unmaskedContext = getUnmaskedContext();
|
||||
const context = {};
|
||||
|
||||
for (let key in contextTypes) {
|
||||
context[key] = unmaskedContext[key];
|
||||
}
|
||||
@@ -62,7 +65,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) {
|
||||
};
|
||||
|
||||
exports.hasContextChanged = function() : boolean {
|
||||
return index > -1 && didPerformWorkStack[index];
|
||||
return didPerformWorkStackCursor.current;
|
||||
};
|
||||
|
||||
function isContextProvider(fiber : Fiber) : boolean {
|
||||
@@ -75,19 +78,17 @@ function isContextProvider(fiber : Fiber) : boolean {
|
||||
}
|
||||
exports.isContextProvider = isContextProvider;
|
||||
|
||||
function popContextProvider() : void {
|
||||
invariant(index > -1, 'Unexpected context pop');
|
||||
contextStack[index] = emptyObject;
|
||||
didPerformWorkStack[index] = false;
|
||||
index--;
|
||||
function popContextProvider(fiber : Fiber) : void {
|
||||
pop(didPerformWorkStackCursor, fiber);
|
||||
pop(contextStackCursor, fiber);
|
||||
}
|
||||
exports.popContextProvider = popContextProvider;
|
||||
|
||||
exports.pushTopLevelContextObject = function(context : Object, didChange : boolean) : void {
|
||||
invariant(index === -1, 'Unexpected context found on stack');
|
||||
index++;
|
||||
contextStack[index] = context;
|
||||
didPerformWorkStack[index] = didChange;
|
||||
exports.pushTopLevelContextObject = function(fiber : Fiber, context : Object, didChange : boolean) : void {
|
||||
invariant(contextStackCursor.cursor == null, 'Unexpected context found on stack');
|
||||
|
||||
push(contextStackCursor, context, fiber);
|
||||
push(didPerformWorkStackCursor, didChange, fiber);
|
||||
};
|
||||
|
||||
function processChildContext(fiber : Fiber, parentContext : Object, isReconciling : boolean): Object {
|
||||
@@ -129,13 +130,13 @@ exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork :
|
||||
instance.__reactInternalMemoizedMergedChildContext = mergedContext;
|
||||
}
|
||||
|
||||
index++;
|
||||
contextStack[index] = mergedContext;
|
||||
didPerformWorkStack[index] = didPerformWork;
|
||||
push(contextStackCursor, mergedContext, workInProgress);
|
||||
push(didPerformWorkStackCursor, didPerformWork, workInProgress);
|
||||
};
|
||||
|
||||
exports.resetContext = function() : void {
|
||||
index = -1;
|
||||
contextStackCursor.current = null;
|
||||
didPerformWorkStackCursor.current = false;
|
||||
};
|
||||
|
||||
exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object {
|
||||
@@ -157,13 +158,3 @@ exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object {
|
||||
}
|
||||
return node.stateNode.context;
|
||||
};
|
||||
|
||||
exports.unwindContext = function(from : Fiber, to: Fiber) {
|
||||
let node = from;
|
||||
while (node && (node !== to) && (node.alternate !== to)) {
|
||||
if (isContextProvider(node)) {
|
||||
popContextProvider();
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,16 +14,23 @@
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
import type { StackCursor } from 'ReactFiberStack';
|
||||
|
||||
const emptyObject = require('emptyObject');
|
||||
|
||||
const {
|
||||
createCursor,
|
||||
pop,
|
||||
push,
|
||||
} = require('ReactFiberStack');
|
||||
|
||||
export type HostContext<C, CX> = {
|
||||
getRootHostContainer() : C,
|
||||
getHostContext() : CX,
|
||||
|
||||
pushHostContext(fiber : Fiber) : void,
|
||||
getRootHostContainer() : C,
|
||||
popHostContainer(fiber : Fiber) : void,
|
||||
popHostContext(fiber : Fiber) : void,
|
||||
|
||||
pushHostContainer(container : C) : void,
|
||||
popHostContainer() : void,
|
||||
pushHostContainer(fiber : Fiber, container : C) : void,
|
||||
pushHostContext(fiber : Fiber) : void,
|
||||
resetHostContainer() : void,
|
||||
};
|
||||
|
||||
@@ -35,129 +42,88 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
getRootHostContext,
|
||||
} = config;
|
||||
|
||||
// Context stack is reused across the subtrees.
|
||||
// We use a null sentinel on the fiber stack to separate them.
|
||||
let contextFibers : Array<Fiber | null> = [];
|
||||
let contextValues : Array<CX | null> = [];
|
||||
let contextDepth : number = -1;
|
||||
// Current context for fast access.
|
||||
let currentContextValue : CX | null = null;
|
||||
// Current root instance for fast access.
|
||||
let rootInstance : C | null = null;
|
||||
// A stack of outer root instances if we're in a portal.
|
||||
let portalStack : Array<C | null> = [];
|
||||
let portalDepth : number = -1;
|
||||
let contextStackCursor : StackCursor<?CX> = createCursor((null: ?CX));
|
||||
let contextFiberStackCursor : StackCursor<?Fiber> = createCursor((null: ?Fiber));
|
||||
let rootInstanceStackCursor : StackCursor<?C> = createCursor((null: ?C));
|
||||
|
||||
function getRootHostContainer() : C {
|
||||
const rootInstance = rootInstanceStackCursor.current;
|
||||
if (rootInstance == null) {
|
||||
throw new Error('Expected root container to exist.');
|
||||
}
|
||||
return rootInstance;
|
||||
}
|
||||
|
||||
function pushHostContainer(nextRootInstance : C) {
|
||||
function pushHostContainer(fiber : Fiber, nextRootInstance : C) {
|
||||
// Push current root instance onto the stack;
|
||||
// This allows us to reset root when portals are popped.
|
||||
push(rootInstanceStackCursor, nextRootInstance, fiber);
|
||||
|
||||
const nextRootContext = getRootHostContext(nextRootInstance);
|
||||
if (rootInstance == null) {
|
||||
// We're entering a root.
|
||||
rootInstance = nextRootInstance;
|
||||
} else {
|
||||
// We're entering a portal.
|
||||
// Save the current root to the portal stack.
|
||||
portalDepth++;
|
||||
portalStack[portalDepth] = rootInstance;
|
||||
rootInstance = nextRootInstance;
|
||||
}
|
||||
// Push the next root or portal context.
|
||||
contextDepth++;
|
||||
contextFibers[contextDepth] = null;
|
||||
contextValues[contextDepth] = nextRootContext;
|
||||
currentContextValue = nextRootContext;
|
||||
|
||||
// Track the context and the Fiber that provided it.
|
||||
// This enables us to pop only Fibers that provide unique contexts.
|
||||
push(contextFiberStackCursor, fiber, fiber);
|
||||
push(contextStackCursor, nextRootContext, fiber);
|
||||
}
|
||||
|
||||
function popHostContainer() {
|
||||
if (portalDepth === -1) {
|
||||
// We're popping the root.
|
||||
rootInstance = null;
|
||||
currentContextValue = null;
|
||||
contextDepth = -1;
|
||||
} else {
|
||||
// We're popping a portal.
|
||||
// Restore the root instance.
|
||||
rootInstance = portalStack[portalDepth];
|
||||
portalStack[portalDepth] = null;
|
||||
portalDepth--;
|
||||
// If we pushed any context while in a portal, we need to roll it back.
|
||||
if (contextDepth > -1) {
|
||||
contextDepth--;
|
||||
if (contextDepth > -1) {
|
||||
currentContextValue = contextValues[contextDepth];
|
||||
} else {
|
||||
currentContextValue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function popHostContainer(fiber : Fiber) {
|
||||
pop(contextStackCursor, fiber);
|
||||
pop(contextFiberStackCursor, fiber);
|
||||
pop(rootInstanceStackCursor, fiber);
|
||||
}
|
||||
|
||||
function getHostContext() : CX {
|
||||
if (currentContextValue == null) {
|
||||
const context = contextStackCursor.current;
|
||||
if (context == null) {
|
||||
throw new Error('Expected host context to exist.');
|
||||
}
|
||||
return currentContextValue;
|
||||
return context;
|
||||
}
|
||||
|
||||
function pushHostContext(fiber : Fiber) : void {
|
||||
if (currentContextValue == null) {
|
||||
const rootInstance = rootInstanceStackCursor.current;
|
||||
if (rootInstance == null) {
|
||||
throw new Error('Expected root host context to exist.');
|
||||
}
|
||||
const nextContextValue = getChildHostContext(currentContextValue, fiber.type, rootInstance);
|
||||
if (currentContextValue === nextContextValue) {
|
||||
|
||||
const context = contextStackCursor.current || emptyObject;
|
||||
const nextContext = getChildHostContext(context, fiber.type, rootInstance);
|
||||
|
||||
// Don't push this Fiber's context unless it's unique.
|
||||
if (context === nextContext) {
|
||||
return;
|
||||
}
|
||||
contextDepth++;
|
||||
contextFibers[contextDepth] = fiber;
|
||||
contextValues[contextDepth] = nextContextValue;
|
||||
currentContextValue = nextContextValue;
|
||||
|
||||
// Track the context and the Fiber that provided it.
|
||||
// This enables us to pop only Fibers that provide unique contexts.
|
||||
push(contextFiberStackCursor, fiber, fiber);
|
||||
push(contextStackCursor, nextContext, fiber);
|
||||
}
|
||||
|
||||
function popHostContext(fiber : Fiber) : void {
|
||||
if (contextDepth === -1) {
|
||||
return;
|
||||
}
|
||||
if (contextFibers == null || contextValues == null) {
|
||||
throw new Error('Expected host context stacks to exist when index is more than -1.');
|
||||
}
|
||||
if (fiber !== contextFibers[contextDepth]) {
|
||||
// Do not pop unless this Fiber provided the current context.
|
||||
// pushHostContext() only pushes Fibers that provide unique contexts.
|
||||
if (contextFiberStackCursor.current !== fiber) {
|
||||
return;
|
||||
}
|
||||
|
||||
contextFibers[contextDepth] = null;
|
||||
contextValues[contextDepth] = null;
|
||||
contextDepth--;
|
||||
if (contextDepth > -1) {
|
||||
currentContextValue = contextValues[contextDepth];
|
||||
} else {
|
||||
currentContextValue = null;
|
||||
}
|
||||
pop(contextStackCursor, fiber);
|
||||
pop(contextFiberStackCursor, fiber);
|
||||
}
|
||||
|
||||
function resetHostContainer() {
|
||||
// Reset portal stack pointer because we're starting from the very top.
|
||||
portalDepth = -1;
|
||||
// Reset current container state.
|
||||
rootInstance = null;
|
||||
contextDepth = -1;
|
||||
currentContextValue = null;
|
||||
contextStackCursor.current = null;
|
||||
rootInstanceStackCursor.current = null;
|
||||
}
|
||||
|
||||
return {
|
||||
getRootHostContainer,
|
||||
getHostContext,
|
||||
|
||||
pushHostContext,
|
||||
popHostContext,
|
||||
|
||||
pushHostContainer,
|
||||
getRootHostContainer,
|
||||
popHostContainer,
|
||||
popHostContext,
|
||||
pushHostContainer,
|
||||
pushHostContext,
|
||||
resetHostContainer,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,6 +17,12 @@ import type { FiberRoot } from 'ReactFiberRoot';
|
||||
import type { HostConfig, Deadline } from 'ReactFiberReconciler';
|
||||
import type { PriorityLevel } from 'ReactPriorityLevel';
|
||||
|
||||
var {
|
||||
isContextProvider,
|
||||
popContextProvider,
|
||||
} = require('ReactFiberContext');
|
||||
const { reset } = require('ReactFiberStack');
|
||||
|
||||
var ReactFiberBeginWork = require('ReactFiberBeginWork');
|
||||
var ReactFiberCompleteWork = require('ReactFiberCompleteWork');
|
||||
var ReactFiberCommitWork = require('ReactFiberCommitWork');
|
||||
@@ -59,7 +65,6 @@ var {
|
||||
|
||||
var {
|
||||
resetContext,
|
||||
unwindContext,
|
||||
} = require('ReactFiberContext');
|
||||
|
||||
if (__DEV__) {
|
||||
@@ -155,6 +160,9 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
|
||||
}
|
||||
|
||||
function resetContextStack() {
|
||||
// Reset the stack
|
||||
reset();
|
||||
// Reset the cursors
|
||||
resetContext();
|
||||
resetHostContainer();
|
||||
}
|
||||
@@ -724,8 +732,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
|
||||
// from the root. Can't do that until then because without memoized
|
||||
// props, the nodes higher up in the tree will rerender unnecessarily.
|
||||
if (failedWork) {
|
||||
unwindContext(failedWork, boundary);
|
||||
unwindHostContext(failedWork, boundary);
|
||||
unwindContexts(failedWork, boundary);
|
||||
}
|
||||
nextUnitOfWork = completeUnitOfWork(boundary);
|
||||
}
|
||||
@@ -945,18 +952,23 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
|
||||
}
|
||||
}
|
||||
|
||||
function unwindHostContext(from : Fiber, to: Fiber) {
|
||||
function unwindContexts(from : Fiber, to: Fiber) {
|
||||
let node = from;
|
||||
while (node && (node !== to) && (node.alternate !== to)) {
|
||||
switch (node.tag) {
|
||||
case ClassComponent:
|
||||
if (isContextProvider(node)) {
|
||||
popContextProvider(node);
|
||||
}
|
||||
break;
|
||||
case HostComponent:
|
||||
popHostContext(node);
|
||||
break;
|
||||
case HostRoot:
|
||||
popHostContainer();
|
||||
popHostContainer(node);
|
||||
break;
|
||||
case HostPortal:
|
||||
popHostContainer();
|
||||
popHostContainer(node);
|
||||
break;
|
||||
}
|
||||
node = node.return;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactFiberStack
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
|
||||
export type StackCursor<T> = {
|
||||
current: T
|
||||
};
|
||||
|
||||
const warning = require('warning');
|
||||
|
||||
const valueStack : Array<any> = [];
|
||||
|
||||
if (__DEV__) {
|
||||
var fiberStack : Array<Fiber | null> = [];
|
||||
}
|
||||
|
||||
let index = -1;
|
||||
|
||||
exports.createCursor = function<T>(defaultValue : T) : StackCursor<T> {
|
||||
return {
|
||||
current: defaultValue,
|
||||
};
|
||||
};
|
||||
|
||||
exports.isEmpty = function() : boolean {
|
||||
return index === -1;
|
||||
};
|
||||
|
||||
exports.pop = function<T>(
|
||||
cursor : StackCursor<T>,
|
||||
fiber: Fiber,
|
||||
) : void {
|
||||
if (index < 0) {
|
||||
if (__DEV__) {
|
||||
warning(false, 'Unexpected pop.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (fiber !== fiberStack[index]) {
|
||||
warning(false, 'Unexpected Fiber popped.');
|
||||
}
|
||||
}
|
||||
|
||||
cursor.current = valueStack[index];
|
||||
|
||||
valueStack[index] = null;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = null;
|
||||
}
|
||||
|
||||
index--;
|
||||
};
|
||||
|
||||
exports.push = function<T>(
|
||||
cursor : StackCursor<T>,
|
||||
value : T,
|
||||
fiber: Fiber,
|
||||
) : void {
|
||||
index++;
|
||||
|
||||
valueStack[index] = cursor.current;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = fiber;
|
||||
}
|
||||
|
||||
cursor.current = value;
|
||||
};
|
||||
|
||||
exports.reset = function() : void {
|
||||
while (index > -1) {
|
||||
valueStack[index] = null;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = null;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user