Skip to content

Commit cb6da8e

Browse files
authored
Fiber: renderSubtreeIntoContainer (#8368)
1 parent f686218 commit cb6da8e

14 files changed

+172
-54
lines changed

scripts/fiber/tests-failing.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ src/addons/__tests__/ReactFragment-test.js
66
* should throw if a plain object even if it is in an owner
77
* should throw if a plain object looks like an old element
88

9-
src/addons/__tests__/renderSubtreeIntoContainer-test.js
10-
* should pass context when rendering subtree elsewhere
11-
* should update context if it changes due to setState
12-
* should update context if it changes due to re-render
13-
149
src/isomorphic/classic/__tests__/ReactContextValidator-test.js
1510
* should pass previous context to lifecycles
1611

scripts/fiber/tests-passing.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ src/addons/__tests__/ReactFragment-test.js
4444
* should warn if passing a ReactElement to createFragment
4545

4646
src/addons/__tests__/renderSubtreeIntoContainer-test.js
47+
* should pass context when rendering subtree elsewhere
4748
* should throw if parentComponent is invalid
49+
* should update context if it changes due to setState
50+
* should update context if it changes due to re-render
4851

4952
src/addons/__tests__/update-test.js
5053
* pushes

src/renderers/dom/ReactDOM.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ var ReactVersion = require('ReactVersion');
2323

2424
var findDOMNode = require('findDOMNode');
2525
var getHostComponentFromComposite = require('getHostComponentFromComposite');
26-
var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
2726
var warning = require('warning');
2827

2928
ReactDOMInjection.inject();
@@ -37,7 +36,7 @@ var ReactDOM = {
3736

3837
/* eslint-disable camelcase */
3938
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
40-
unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer,
39+
unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer,
4140
/* eslint-enable camelcase */
4241
};
4342

src/renderers/dom/fiber/ReactDOMFiber.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,22 @@ import type { Fiber } from 'ReactFiber';
1616
import type { HostChildren } from 'ReactFiberReconciler';
1717

1818
var ReactControlledComponent = require('ReactControlledComponent');
19-
var ReactFiberReconciler = require('ReactFiberReconciler');
2019
var ReactDOMComponentTree = require('ReactDOMComponentTree');
2120
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
2221
var ReactDOMFiberComponent = require('ReactDOMFiberComponent');
2322
var ReactDOMInjection = require('ReactDOMInjection');
23+
var ReactFiberReconciler = require('ReactFiberReconciler');
24+
var ReactInstanceMap = require('ReactInstanceMap');
2425

2526
var findDOMNode = require('findDOMNode');
27+
var invariant = require('invariant');
2628
var warning = require('warning');
2729

30+
ReactDOMInjection.inject();
31+
ReactControlledComponent.injection.injectFiberControlledHostComponent(
32+
ReactDOMFiberComponent
33+
);
34+
2835
var {
2936
createElement,
3037
setInitialProperties,
@@ -147,18 +154,29 @@ function warnAboutUnstableUse() {
147154
warned = true;
148155
}
149156

157+
function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any, any>, element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
158+
let root;
159+
if (!container._reactRootContainer) {
160+
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, parentComponent, callback);
161+
} else {
162+
DOMRenderer.updateContainer(element, root = container._reactRootContainer, parentComponent, callback);
163+
}
164+
return DOMRenderer.getPublicRootInstance(root);
165+
}
166+
150167
var ReactDOM = {
151168

152169
render(element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
153170
warnAboutUnstableUse();
154-
let root;
171+
return renderSubtreeIntoContainer(null, element, container, callback);
172+
},
155173

156-
if (!container._reactRootContainer) {
157-
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, callback);
158-
} else {
159-
DOMRenderer.updateContainer(element, root = container._reactRootContainer, callback);
160-
}
161-
return DOMRenderer.getPublicRootInstance(root);
174+
unstable_renderSubtreeIntoContainer(parentComponent : ReactComponent<any, any, any>, element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
175+
invariant(
176+
parentComponent != null && ReactInstanceMap.has(parentComponent),
177+
'parentComponent must be a valid React Component'
178+
);
179+
return renderSubtreeIntoContainer(parentComponent, element, container, callback);
162180
},
163181

164182
unmountComponentAtNode(container : DOMContainerElement) {

src/renderers/dom/stack/client/ReactMount.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var ReactReconciler = require('ReactReconciler');
2727
var ReactUpdateQueue = require('ReactUpdateQueue');
2828
var ReactUpdates = require('ReactUpdates');
2929

30-
var emptyObject = require('emptyObject');
30+
var getContextForSubtree = require('getContextForSubtree');
3131
var instantiateReactComponent = require('instantiateReactComponent');
3232
var invariant = require('invariant');
3333
var setInnerHTML = require('setInnerHTML');
@@ -466,14 +466,7 @@ var ReactMount = {
466466
{ child: nextElement }
467467
);
468468

469-
var nextContext;
470-
if (parentComponent) {
471-
var parentInst = ReactInstanceMap.get(parentComponent);
472-
nextContext = parentInst._processChildContext(parentInst._context);
473-
} else {
474-
nextContext = emptyObject;
475-
}
476-
469+
var nextContext = getContextForSubtree(parentComponent);
477470
var prevComponent = getTopLevelWrapperInContainer(container);
478471

479472
if (prevComponent) {

src/renderers/dom/stack/client/renderSubtreeIntoContainer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111

1212
'use strict';
1313

14-
var ReactMount = require('ReactMount');
14+
var ReactDOM = require('ReactDOM');
1515

16-
module.exports = ReactMount.renderSubtreeIntoContainer;
16+
module.exports = ReactDOM.unstable_renderSubtreeIntoContainer;

src/renderers/noop/ReactNoop.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ var ReactNoop = {
182182
if (!roots.has(rootID)) {
183183
const container = { rootID: rootID, children: [] };
184184
rootContainers.set(rootID, container);
185-
const root = NoopRenderer.mountContainer(element, container, callback);
185+
const root = NoopRenderer.mountContainer(element, container, null, callback);
186186
roots.set(rootID, root);
187187
} else {
188188
const root = roots.get(rootID);
189189
if (root) {
190-
NoopRenderer.updateContainer(element, root, callback);
190+
NoopRenderer.updateContainer(element, root, null, callback);
191191
}
192192
}
193193
},

src/renderers/shared/fiber/ReactFiberBeginWork.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import type { ReactCoroutine } from 'ReactCoroutine';
1616
import type { Fiber } from 'ReactFiber';
17+
import type { FiberRoot } from 'ReactFiberRoot';
1718
import type { HostConfig } from 'ReactFiberReconciler';
1819
import type { PriorityLevel } from 'ReactPriorityLevel';
1920

@@ -30,6 +31,7 @@ var {
3031
isContextProvider,
3132
hasContextChanged,
3233
pushContextProvider,
34+
pushTopLevelContextObject,
3335
resetContext,
3436
} = require('ReactFiberContext');
3537
var {
@@ -411,11 +413,21 @@ module.exports = function<T, P, I, TI, C>(
411413
return updateFunctionalComponent(current, workInProgress);
412414
case ClassComponent:
413415
return updateClassComponent(current, workInProgress);
414-
case HostContainer:
416+
case HostContainer: {
417+
const root = (workInProgress.stateNode : FiberRoot);
418+
if (root.pendingContext) {
419+
pushTopLevelContextObject(
420+
root.pendingContext,
421+
root.pendingContext !== root.context
422+
);
423+
} else {
424+
pushTopLevelContextObject(root.context, false);
425+
}
415426
reconcileChildren(current, workInProgress, workInProgress.pendingProps);
416427
// A yield component is just a placeholder, we can just run through the
417428
// next one immediately.
418429
return workInProgress.child;
430+
}
419431
case HostComponent:
420432
if (workInProgress.stateNode && typeof config.beginUpdate === 'function') {
421433
config.beginUpdate(workInProgress.stateNode);

src/renderers/shared/fiber/ReactFiberCompleteWork.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import type { ReactCoroutine } from 'ReactCoroutine';
1616
import type { Fiber } from 'ReactFiber';
17+
import type { FiberRoot } from 'ReactFiberRoot';
1718
import type { HostConfig } from 'ReactFiberReconciler';
1819
import type { ReifiedYield } from 'ReactReifiedYield';
1920

@@ -157,15 +158,22 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
157158
markCallback(workInProgress);
158159
}
159160
return null;
160-
case HostContainer:
161+
case HostContainer: {
161162
transferOutput(workInProgress.child, workInProgress);
163+
popContextProvider();
164+
const fiberRoot = (workInProgress.stateNode : FiberRoot);
165+
if (fiberRoot.pendingContext) {
166+
fiberRoot.context = fiberRoot.pendingContext;
167+
fiberRoot.pendingContext = null;
168+
}
162169
// We don't know if a container has updated any children so we always
163170
// need to update it right now. We schedule this side-effect before
164171
// all the other side-effects in the subtree. We need to schedule it
165172
// before so that the entire tree is up-to-date before the life-cycles
166173
// are invoked.
167174
markUpdate(workInProgress);
168175
return null;
176+
}
169177
case HostComponent:
170178
let newProps = workInProgress.pendingProps;
171179
if (current && workInProgress.stateNode != null) {

src/renderers/shared/fiber/ReactFiberContext.js

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var emptyObject = require('emptyObject');
1818
var invariant = require('invariant');
1919
var {
2020
getComponentName,
21+
isFiberMounted,
2122
} = require('ReactFiberTreeReflection');
2223
var {
2324
ClassComponent,
@@ -64,45 +65,58 @@ exports.hasContextChanged = function() : boolean {
6465
return index > -1 && didPerformWorkStack[index];
6566
};
6667

67-
exports.isContextProvider = function(fiber : Fiber) : boolean {
68+
function isContextProvider(fiber : Fiber) : boolean {
6869
return (
6970
fiber.tag === ClassComponent &&
7071
typeof fiber.stateNode.getChildContext === 'function'
7172
);
72-
};
73+
}
74+
exports.isContextProvider = isContextProvider;
7375

7476
exports.popContextProvider = function() : void {
7577
contextStack[index] = emptyObject;
7678
didPerformWorkStack[index] = false;
7779
index--;
7880
};
7981

80-
exports.pushContextProvider = function(fiber : Fiber, didPerformWork : boolean) : void {
82+
exports.pushTopLevelContextObject = function(context : Object, didChange : boolean) : void {
83+
invariant(index === -1, 'Unexpected context found on stack');
84+
index++;
85+
contextStack[index] = context;
86+
didPerformWorkStack[index] = didChange;
87+
};
88+
89+
function processChildContext(fiber : Fiber, parentContext : Object): Object {
8190
const instance = fiber.stateNode;
8291
const childContextTypes = fiber.type.childContextTypes;
92+
const childContext = instance.getChildContext();
93+
for (let contextKey in childContext) {
94+
invariant(
95+
contextKey in childContextTypes,
96+
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
97+
getComponentName(fiber),
98+
contextKey
99+
);
100+
}
101+
if (__DEV__) {
102+
const name = getComponentName(fiber);
103+
const debugID = 0; // TODO: pass a real ID
104+
checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, debugID);
105+
}
106+
return {...parentContext, ...childContext};
107+
}
108+
exports.processChildContext = processChildContext;
83109

110+
exports.pushContextProvider = function(fiber : Fiber, didPerformWork : boolean) : void {
111+
const instance = fiber.stateNode;
84112
const memoizedMergedChildContext = instance.__reactInternalMemoizedMergedChildContext;
85113
const canReuseMergedChildContext = !didPerformWork && memoizedMergedChildContext != null;
86114

87115
let mergedContext = null;
88116
if (canReuseMergedChildContext) {
89117
mergedContext = memoizedMergedChildContext;
90118
} else {
91-
const childContext = instance.getChildContext();
92-
for (let contextKey in childContext) {
93-
invariant(
94-
contextKey in childContextTypes,
95-
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
96-
getComponentName(fiber),
97-
contextKey
98-
);
99-
}
100-
if (__DEV__) {
101-
const name = getComponentName(fiber);
102-
const debugID = 0; // TODO: pass a real ID
103-
checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, debugID);
104-
}
105-
mergedContext = {...getUnmaskedContext(), ...childContext};
119+
mergedContext = processChildContext(fiber, getUnmaskedContext());
106120
instance.__reactInternalMemoizedMergedChildContext = mergedContext;
107121
}
108122

@@ -115,3 +129,20 @@ exports.resetContext = function() : void {
115129
index = -1;
116130
};
117131

132+
exports.findCurrentUnmaskedContext = function(fiber: Fiber) : Object {
133+
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
134+
// makes sense elsewhere
135+
invariant(
136+
isFiberMounted(fiber) && fiber.tag === ClassComponent,
137+
'Expected subtree parent to be a mounted class component'
138+
);
139+
140+
let node : ?Fiber = parent;
141+
while (node) {
142+
if (isContextProvider(fiber)) {
143+
return fiber.stateNode.__reactInternalMemoizedMergedChildContext;
144+
}
145+
node = node.return;
146+
}
147+
return emptyObject;
148+
};

0 commit comments

Comments
 (0)