[Fiber] Add top level render callbacks into ReactDOMFiber and ReactNoop (#8102)
* [Fiber] Add top level render callbacks into ReactDOMFiber and ReactNoop * [Fiber] Support multiple render callbacks * [Fiber] `this` in render callbacks are public instances * [Fiber] commitLifeCycles move to behind the effectTag check
Toru Kobayashi committed
Oct 27, 2016 at 02:19 UTC
2ef12084e4bdaca980bee0f404cada005519ffbb
8 files changed
+65
-13
src/renderers/dom/fiber/ReactDOMFiber.js
+3
-3
@@ -124,13 +124,13 @@ function warnAboutUnstableUse() {
124
125
var ReactDOM = {
126
127
- render(element : ReactElement<any>, container : DOMContainerElement) {
127
+ render(element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
128
warnAboutUnstableUse();
129
let root;
130
if (!container._reactRootContainer) {
131
- root = container._reactRootContainer = DOMRenderer.mountContainer(element, container);
131
+ root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, callback);
132
} else {
133
- DOMRenderer.updateContainer(element, root = container._reactRootContainer);
133
+ DOMRenderer.updateContainer(element, root = container._reactRootContainer, callback);
134
}
135
return DOMRenderer.getPublicRootInstance(root);
136
},
src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
+20
@@ -43,6 +43,26 @@ describe('ReactDOMFiber', () => {
43
expect(container.textContent).toEqual('10');
44
});
45
46
+ it('should be called a callback argument', () => {
47
+ // mounting phase
48
+ let called = false;
49
+ ReactDOM.render(
50
+ <div>Foo</div>,
51
+ container,
52
+ () => called = true
53
+ );
54
+ expect(called).toEqual(true);
55
+
56
+ // updating phase
57
+ called = false;
58
+ ReactDOM.render(
59
+ <div>Foo</div>,
60
+ container,
61
+ () => called = true
62
+ );
63
+ expect(called).toEqual(true);
64
+ });
65
+
66
if (ReactDOMFeatureFlags.useFiber) {
67
it('should render a component returning strings directly from render', () => {
68
const Text = ({value}) => value;
src/renderers/noop/ReactNoop.js
+3
-3
@@ -146,11 +146,11 @@ var ReactNoop = {
146
147
root: rootContainer,
148
149
- render(element : ReactElement<any>) {
149
+ render(element : ReactElement<any>, callback: ?Function) {
150
if (!root) {
151
- root = NoopRenderer.mountContainer(element, rootContainer);
151
+ root = NoopRenderer.mountContainer(element, rootContainer, callback);
152
} else {
153
- NoopRenderer.updateContainer(element, root);
153
+ NoopRenderer.updateContainer(element, root, callback);
154
}
155
},
156
src/renderers/shared/fiber/ReactFiberCommitWork.js
+8
@@ -322,6 +322,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
322
attachRef(current, finishedWork, instance);
323
return;
324
}
325
+ case HostContainer: {
326
+ const instance = finishedWork.stateNode;
327
+ if (instance.callbackList) {
328
+ const { callbackList } = instance;
329
+ instance.callbackList = null;
330
+ callCallbacks(callbackList, instance.current.child.stateNode);
331
+ }
332
+ }
333
case HostComponent: {
334
const instance : I = finishedWork.stateNode;
335
attachRef(current, finishedWork, instance);
src/renderers/shared/fiber/ReactFiberReconciler.js
+16
-2
@@ -20,6 +20,8 @@ import type { PriorityLevel } from 'ReactPriorityLevel';
20
var { createFiberRoot } = require('ReactFiberRoot');
21
var ReactFiberScheduler = require('ReactFiberScheduler');
22
23
+var { createUpdateQueue, addCallbackToQueue } = require('ReactFiberUpdateQueue');
24
+
25
if (__DEV__) {
26
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
27
}
@@ -79,9 +81,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
81
82
return {
83
82
- mountContainer(element : ReactElement<any>, containerInfo : C) : OpaqueNode {
84
+ mountContainer(element : ReactElement<any>, containerInfo : C, callback: ?Function) : OpaqueNode {
85
const root = createFiberRoot(containerInfo);
86
const container = root.current;
87
+ if (callback) {
88
+ const queue = createUpdateQueue(null);
89
+ addCallbackToQueue(queue, callback);
90
+ root.callbackList = queue;
91
+ }
92
// TODO: Use pending work/state instead of props.
93
// TODO: This should not override the pendingWorkPriority if there is
94
// higher priority work in the subtree.
@@ -99,9 +106,16 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
106
return container;
107
},
108
102
- updateContainer(element : ReactElement<any>, container : OpaqueNode) : void {
109
+ updateContainer(element : ReactElement<any>, container : OpaqueNode, callback: ?Function) : void {
110
// TODO: If this is a nested container, this won't be the root.
111
const root : FiberRoot = (container.stateNode : any);
112
+ if (callback) {
113
+ const queue = root.callbackList ?
114
+ root.callbackList :
115
+ createUpdateQueue(null);
116
+ addCallbackToQueue(queue, callback);
117
+ root.callbackList = queue;
118
+ }
119
// TODO: Use pending work/state instead of props.
120
root.current.pendingProps = element;
121
src/renderers/shared/fiber/ReactFiberRoot.js
+4
@@ -13,6 +13,7 @@
13
'use strict';
14
15
import type { Fiber } from 'ReactFiber';
16
+import type { UpdateQueue } from 'ReactFiberUpdateQueue';
17
18
const { createHostContainerFiber } = require('ReactFiber');
19
@@ -25,6 +26,8 @@ export type FiberRoot = {
26
isScheduled: boolean,
27
// The work schedule is a linked list.
28
nextScheduledRoot: ?FiberRoot,
29
+ // Linked list of callbacks to call after updates are committed.
30
+ callbackList: ?UpdateQueue,
31
};
32
33
exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
@@ -36,6 +39,7 @@ exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
39
containerInfo: containerInfo,
40
isScheduled: false,
41
nextScheduledRoot: null,
42
+ callbackList: null,
43
};
44
uninitializedFiber.stateNode = root;
45
return root;
src/renderers/shared/fiber/ReactFiberScheduler.js
+1
@@ -173,6 +173,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
173
if (finishedWork.effectTag !== NoEffect) {
174
const current = finishedWork.alternate;
175
commitWork(current, finishedWork);
176
+ commitLifeCycles(current, finishedWork);
177
}
178
}
179
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+10
-5
@@ -37,6 +37,7 @@ describe('ReactIncremental', () => {
37
38
it('should render a simple component, in steps if needed', () => {
39
40
+ var renderCallbackCalled = false;
41
var barCalled = false;
42
function Bar() {
43
barCalled = true;
@@ -52,17 +53,20 @@ describe('ReactIncremental', () => {
53
];
54
}
55
55
- ReactNoop.render(<Foo />);
56
+ ReactNoop.render(<Foo />, () => renderCallbackCalled = true);
57
expect(fooCalled).toBe(false);
58
expect(barCalled).toBe(false);
59
+ expect(renderCallbackCalled).toBe(false);
60
// Do one step of work.
61
ReactNoop.flushDeferredPri(7 + 5);
62
expect(fooCalled).toBe(true);
63
expect(barCalled).toBe(false);
64
+ expect(renderCallbackCalled).toBe(false);
65
// Do the rest of the work.
66
ReactNoop.flushDeferredPri(50);
67
expect(fooCalled).toBe(true);
68
expect(barCalled).toBe(true);
69
+ expect(renderCallbackCalled).toBe(true);
70
});
71
72
it('updates a previous render', () => {
@@ -98,21 +102,22 @@ describe('ReactIncremental', () => {
102
);
103
}
104
101
- ReactNoop.render(<Foo text="foo" />);
105
+ ReactNoop.render(<Foo text="foo" />, () => ops.push('renderCallbackCalled'));
106
ReactNoop.flush();
107
104
- expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer']);
108
+ expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer', 'renderCallbackCalled']);
109
110
ops = [];
111
108
- ReactNoop.render(<Foo text="bar" />);
112
+ ReactNoop.render(<Foo text="bar" />, () => ops.push('firstRenderCallbackCalled'));
113
+ ReactNoop.render(<Foo text="bar" />, () => ops.push('secondRenderCallbackCalled'));
114
ReactNoop.flush();
115
116
// TODO: Test bail out of host components. This is currently unobservable.
117
118
// Since this is an update, it should bail out and reuse the work from
119
// Header and Content.
115
- expect(ops).toEqual(['Foo', 'Content']);
120
+ expect(ops).toEqual(['Foo', 'Content', 'firstRenderCallbackCalled', 'secondRenderCallbackCalled']);
121
122
});
123