@samitouri / QOS-React / commits / 2ef12084e4

[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