Skip to content

Commit 465047a

Browse files
committed
[Fiber] Add top level render callbacks into ReactDOMFiber and ReactNoop
1 parent 51e1937 commit 465047a

8 files changed

Lines changed: 66 additions & 13 deletions

File tree

src/renderers/dom/fiber/ReactDOMFiber.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,13 @@ function warnAboutUnstableUse() {
124124

125125
var ReactDOM = {
126126

127-
render(element : ReactElement<any>, container : DOMContainerElement) {
127+
render(element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
128128
warnAboutUnstableUse();
129129
let root;
130130
if (!container._reactRootContainer) {
131-
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container);
131+
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, callback);
132132
} else {
133-
DOMRenderer.updateContainer(element, root = container._reactRootContainer);
133+
DOMRenderer.updateContainer(element, root = container._reactRootContainer, callback);
134134
}
135135
return DOMRenderer.getPublicRootInstance(root);
136136
},

src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ describe('ReactDOMFiber', () => {
4343
expect(container.textContent).toEqual('10');
4444
});
4545

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+
4666
if (ReactDOMFeatureFlags.useFiber) {
4767
it('should render a component returning strings directly from render', () => {
4868
const Text = ({value}) => value;

src/renderers/noop/ReactNoop.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ var ReactNoop = {
146146

147147
root: rootContainer,
148148

149-
render(element : ReactElement<any>) {
149+
render(element : ReactElement<any>, callback: ?Function) {
150150
if (!root) {
151-
root = NoopRenderer.mountContainer(element, rootContainer);
151+
root = NoopRenderer.mountContainer(element, rootContainer, callback);
152152
} else {
153-
NoopRenderer.updateContainer(element, root);
153+
NoopRenderer.updateContainer(element, root, callback);
154154
}
155155
},
156156

src/renderers/shared/fiber/ReactFiberCommitWork.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
308308
attachRef(current, finishedWork, instance);
309309
return;
310310
}
311+
case HostContainer: {
312+
const instance = finishedWork.stateNode;
313+
if (instance.callbackList) {
314+
const { callbackList } = instance;
315+
instance.callbackList = null;
316+
callCallbacks(callbackList, instance);
317+
}
318+
}
311319
case HostComponent: {
312320
const instance : I = finishedWork.stateNode;
313321
attachRef(current, finishedWork, instance);

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import type { PriorityLevel } from 'ReactPriorityLevel';
2020
var { createFiberRoot } = require('ReactFiberRoot');
2121
var ReactFiberScheduler = require('ReactFiberScheduler');
2222

23+
var { createUpdateQueue, addCallbackToQueue } = require('ReactFiberUpdateQueue');
24+
2325
if (__DEV__) {
2426
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
2527
}
@@ -74,9 +76,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
7476

7577
return {
7678

77-
mountContainer(element : ReactElement<any>, containerInfo : C) : OpaqueNode {
79+
mountContainer(element : ReactElement<any>, containerInfo : C, callback: ?Function) : OpaqueNode {
7880
const root = createFiberRoot(containerInfo);
7981
const container = root.current;
82+
if (callback) {
83+
const queue = createUpdateQueue(null);
84+
addCallbackToQueue(queue, callback);
85+
root.callbackList = queue;
86+
}
8087
// TODO: Use pending work/state instead of props.
8188
// TODO: This should not override the pendingWorkPriority if there is
8289
// higher priority work in the subtree.
@@ -94,9 +101,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
94101
return container;
95102
},
96103

97-
updateContainer(element : ReactElement<any>, container : OpaqueNode) : void {
104+
updateContainer(element : ReactElement<any>, container : OpaqueNode, callback: ?Function) : void {
98105
// TODO: If this is a nested container, this won't be the root.
99106
const root : FiberRoot = (container.stateNode : any);
107+
if (callback) {
108+
const queue = createUpdateQueue(null);
109+
addCallbackToQueue(queue, callback);
110+
root.callbackList = queue;
111+
}
100112
// TODO: Use pending work/state instead of props.
101113
root.current.pendingProps = element;
102114

src/renderers/shared/fiber/ReactFiberRoot.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
'use strict';
1414

1515
import type { Fiber } from 'ReactFiber';
16+
import type { UpdateQueue } from 'ReactFiberUpdateQueue';
1617

1718
const { createHostContainerFiber } = require('ReactFiber');
1819

@@ -25,6 +26,8 @@ export type FiberRoot = {
2526
isScheduled: boolean,
2627
// The work schedule is a linked list.
2728
nextScheduledRoot: ?FiberRoot,
29+
// Linked list of callbacks to call after updates are committed.
30+
callbackList: ?UpdateQueue,
2831
};
2932

3033
exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
@@ -36,6 +39,7 @@ exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
3639
containerInfo: containerInfo,
3740
isScheduled: false,
3841
nextScheduledRoot: null,
42+
callbackList: null,
3943
};
4044
uninitializedFiber.stateNode = root;
4145
return root;

src/renderers/shared/fiber/ReactFiberScheduler.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
165165
const current = finishedWork.alternate;
166166
commitWork(current, finishedWork);
167167
}
168+
// if the root is a HostContainer, it may have a callback.
169+
if (finishedWork.tag === HostContainer) {
170+
const current = finishedWork.alternate;
171+
commitLifeCycles(current, finishedWork);
172+
}
168173
}
169174

170175
function resetWorkPriority(workInProgress : Fiber) {

src/renderers/shared/fiber/__tests__/ReactIncremental-test.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe('ReactIncremental', () => {
3737

3838
it('should render a simple component, in steps if needed', () => {
3939

40+
var renderCallbackCalled = false;
4041
var barCalled = false;
4142
function Bar() {
4243
barCalled = true;
@@ -52,17 +53,20 @@ describe('ReactIncremental', () => {
5253
];
5354
}
5455

55-
ReactNoop.render(<Foo />);
56+
ReactNoop.render(<Foo />, () => renderCallbackCalled = true);
5657
expect(fooCalled).toBe(false);
5758
expect(barCalled).toBe(false);
59+
expect(renderCallbackCalled).toBe(false);
5860
// Do one step of work.
5961
ReactNoop.flushDeferredPri(7 + 5);
6062
expect(fooCalled).toBe(true);
6163
expect(barCalled).toBe(false);
64+
expect(renderCallbackCalled).toBe(false);
6265
// Do the rest of the work.
6366
ReactNoop.flushDeferredPri(50);
6467
expect(fooCalled).toBe(true);
6568
expect(barCalled).toBe(true);
69+
expect(renderCallbackCalled).toBe(true);
6670
});
6771

6872
it('updates a previous render', () => {
@@ -98,21 +102,21 @@ describe('ReactIncremental', () => {
98102
);
99103
}
100104

101-
ReactNoop.render(<Foo text="foo" />);
105+
ReactNoop.render(<Foo text="foo" />, () => ops.push('renderCallbackCalled'));
102106
ReactNoop.flush();
103107

104-
expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer']);
108+
expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer', 'renderCallbackCalled']);
105109

106110
ops = [];
107111

108-
ReactNoop.render(<Foo text="bar" />);
112+
ReactNoop.render(<Foo text="bar" />, () => ops.push('renderCallbackCalled'));
109113
ReactNoop.flush();
110114

111115
// TODO: Test bail out of host components. This is currently unobservable.
112116

113117
// Since this is an update, it should bail out and reuse the work from
114118
// Header and Content.
115-
expect(ops).toEqual(['Foo', 'Content']);
119+
expect(ops).toEqual(['Foo', 'Content', 'renderCallbackCalled']);
116120

117121
});
118122

0 commit comments

Comments
 (0)