mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Added ReactFiberStack shared by ReactFiberContext and ReactFiberHostContext
ReactFiberStack is the new underlying stack used by ReactFiberContext and ReactFiberHostContext. The goal is to simplify the 2 context managers and to add more errors/dev-warnings when we pop unexpected. This changeset currently causes a lot of tests to fail (as we are currently popping too many times and/or in the wrong order). I'm pushing this branch now to share with Sebastian as he is working on a related cleanup pass at beginWork.
This commit is contained in:
1 parent
a27e4f3361
commit
3d0cf47d26
5 files changed
+183
-121
No files matched your search
@@ -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?
|
||||
@@ -628,4 +633,4 @@ module.exports = function<T, P, I, TI, C, CX>(
|
||||
beginFailedWork,
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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,22 @@ var {
|
||||
ClassComponent,
|
||||
HostRoot,
|
||||
} = require('ReactTypeOfWork');
|
||||
const {
|
||||
createCursor,
|
||||
pop,
|
||||
push,
|
||||
reset,
|
||||
} = require('ReactFiberStack');
|
||||
|
||||
if (__DEV__) {
|
||||
var checkReactTypeSpec = require('checkReactTypeSpec');
|
||||
}
|
||||
|
||||
let index = -1;
|
||||
const contextStack : Array<Object> = [];
|
||||
const didPerformWorkStack : Array<boolean> = [];
|
||||
let contextStackCursor : StackCursor = createCursor();
|
||||
let didPerformWorkStackCursor : StackCursor = createCursor();
|
||||
|
||||
function getUnmaskedContext() {
|
||||
if (index === -1) {
|
||||
return emptyObject;
|
||||
}
|
||||
return contextStack[index];
|
||||
return contextStackCursor.current;
|
||||
}
|
||||
|
||||
exports.getMaskedContext = function(workInProgress : Fiber) {
|
||||
@@ -62,7 +65,7 @@ exports.getMaskedContext = function(workInProgress : Fiber) {
|
||||
};
|
||||
|
||||
exports.hasContextChanged = function() : boolean {
|
||||
return index > -1 && didPerformWorkStack[index];
|
||||
return Boolean(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;
|
||||
reset(contextStackCursor);
|
||||
reset(didPerformWorkStackCursor);
|
||||
};
|
||||
|
||||
exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object {
|
||||
@@ -162,7 +163,7 @@ exports.unwindContext = function(from : Fiber, to: Fiber) {
|
||||
let node = from;
|
||||
while (node && (node !== to) && (node.alternate !== to)) {
|
||||
if (isContextProvider(node)) {
|
||||
popContextProvider();
|
||||
popContextProvider(node);
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
|
||||
@@ -14,16 +14,22 @@
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
import type { StackCursor } from 'ReactFiberStack';
|
||||
|
||||
const {
|
||||
createCursor,
|
||||
pop,
|
||||
push,
|
||||
reset,
|
||||
} = require('ReactFiberStack');
|
||||
|
||||
export type HostContext<C, CX> = {
|
||||
getRootHostContainer() : C,
|
||||
getHostContext() : CX,
|
||||
|
||||
pushHostContext(fiber : Fiber) : void,
|
||||
popHostContext(fiber : Fiber) : void,
|
||||
|
||||
pushHostContainer(container : C) : void,
|
||||
getRootHostContainer() : C,
|
||||
popHostContainer() : void,
|
||||
popHostContext(fiber : Fiber) : void,
|
||||
pushHostContainer(container : C) : void,
|
||||
pushHostContext(fiber : Fiber) : void,
|
||||
resetHostContainer() : void,
|
||||
};
|
||||
|
||||
@@ -35,129 +41,77 @@ 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 = createCursor();
|
||||
let rootInstanceStackCursor : StackCursor = createCursor();
|
||||
|
||||
function getRootHostContainer() : C {
|
||||
if (rootInstance == null) {
|
||||
if (rootInstanceStackCursor.current == null) {
|
||||
throw new Error('Expected root container to exist.');
|
||||
}
|
||||
return rootInstance;
|
||||
return rootInstanceStackCursor.current;
|
||||
}
|
||||
|
||||
// TODO (bvaughn) Pass the host container fiber to push()
|
||||
function pushHostContainer(nextRootInstance : C) {
|
||||
// Push current root instance onto the stack;
|
||||
// This allows us to reset root when portals are popped.
|
||||
push(rootInstanceStackCursor, nextRootInstance, null);
|
||||
|
||||
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;
|
||||
|
||||
push(contextStackCursor, nextRootContext, null);
|
||||
}
|
||||
|
||||
// TODO (bvaughn) Pass the host container fiber to pop()
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
pop(contextStackCursor, null);
|
||||
pop(rootInstanceStackCursor, null);
|
||||
}
|
||||
|
||||
function getHostContext() : CX {
|
||||
if (currentContextValue == null) {
|
||||
if (contextStackCursor.current == null) {
|
||||
throw new Error('Expected host context to exist.');
|
||||
}
|
||||
return currentContextValue;
|
||||
|
||||
return contextStackCursor.current;
|
||||
}
|
||||
|
||||
function pushHostContext(fiber : Fiber) : void {
|
||||
if (currentContextValue == null) {
|
||||
if (rootInstanceStackCursor.current == null) {
|
||||
throw new Error('Expected root host context to exist.');
|
||||
}
|
||||
const nextContextValue = getChildHostContext(currentContextValue, fiber.type, rootInstance);
|
||||
if (currentContextValue === nextContextValue) {
|
||||
|
||||
const context = contextStackCursor.current;
|
||||
const rootInstance = rootInstanceStackCursor.current;
|
||||
const nextContext = getChildHostContext(context, fiber.type, rootInstance);
|
||||
|
||||
if (context === nextContext) {
|
||||
return;
|
||||
}
|
||||
contextDepth++;
|
||||
contextFibers[contextDepth] = fiber;
|
||||
contextValues[contextDepth] = nextContextValue;
|
||||
currentContextValue = nextContextValue;
|
||||
|
||||
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]) {
|
||||
if (contextStackCursor.current == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
contextFibers[contextDepth] = null;
|
||||
contextValues[contextDepth] = null;
|
||||
contextDepth--;
|
||||
if (contextDepth > -1) {
|
||||
currentContextValue = contextValues[contextDepth];
|
||||
} else {
|
||||
currentContextValue = null;
|
||||
}
|
||||
pop(contextStackCursor, 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;
|
||||
reset(contextStackCursor);
|
||||
reset(rootInstanceStackCursor);
|
||||
}
|
||||
|
||||
return {
|
||||
getRootHostContainer,
|
||||
getHostContext,
|
||||
|
||||
pushHostContext,
|
||||
popHostContext,
|
||||
|
||||
pushHostContainer,
|
||||
getRootHostContainer,
|
||||
popHostContainer,
|
||||
popHostContext,
|
||||
pushHostContainer,
|
||||
pushHostContext,
|
||||
resetHostContainer,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 = {|
|
||||
current: any
|
||||
|};
|
||||
|
||||
const emptyObject = require('emptyObject');
|
||||
const warning = require('warning');
|
||||
|
||||
const valueStack : Array<Object> = [];
|
||||
|
||||
if (__DEV__) {
|
||||
var fiberStack : Array<Fiber | null> = [];
|
||||
}
|
||||
|
||||
let index = -1;
|
||||
|
||||
exports.createCursor = function() : StackCursor {
|
||||
return {
|
||||
current: null,
|
||||
};
|
||||
};
|
||||
|
||||
exports.isEmpty = function() : boolean {
|
||||
return index === -1;
|
||||
};
|
||||
|
||||
exports.pop = function(
|
||||
cursor : StackCursor,
|
||||
fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber
|
||||
) : void {
|
||||
if (index < 0) {
|
||||
if (__DEV__) {
|
||||
warning(false, 'Unexpected pop.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (fiber !== fiberStack[index]) {
|
||||
warning(false, 'Unexpected Fiber popped.');
|
||||
}
|
||||
}
|
||||
|
||||
valueStack[index] = emptyObject;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = emptyObject;
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
cursor.current = index > -1
|
||||
? valueStack[index]
|
||||
: null;
|
||||
};
|
||||
|
||||
exports.push = function(
|
||||
cursor : StackCursor,
|
||||
value : any,
|
||||
fiber: Fiber | null, // TODO (bvaughn) Tighten up this type to only accept Fiber
|
||||
) : void {
|
||||
cursor.current = value;
|
||||
|
||||
index++;
|
||||
|
||||
valueStack[index] = value;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = fiber;
|
||||
}
|
||||
};
|
||||
|
||||
exports.reset = function(
|
||||
cursor : StackCursor,
|
||||
) : void {
|
||||
while (index > -1) {
|
||||
valueStack[index] = emptyObject;
|
||||
|
||||
if (__DEV__) {
|
||||
fiberStack[index] = emptyObject;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
cursor.current = null;
|
||||
};
|
||||
Reference in new issue
Block a user