mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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
This commit is contained in:
committed by
Sebastian Markbåge
parent
2ba571c246
commit
2ef12084e4
@@ -124,13 +124,13 @@ function warnAboutUnstableUse() {
|
||||
|
||||
var ReactDOM = {
|
||||
|
||||
render(element : ReactElement<any>, container : DOMContainerElement) {
|
||||
render(element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
|
||||
warnAboutUnstableUse();
|
||||
let root;
|
||||
if (!container._reactRootContainer) {
|
||||
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container);
|
||||
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, callback);
|
||||
} else {
|
||||
DOMRenderer.updateContainer(element, root = container._reactRootContainer);
|
||||
DOMRenderer.updateContainer(element, root = container._reactRootContainer, callback);
|
||||
}
|
||||
return DOMRenderer.getPublicRootInstance(root);
|
||||
},
|
||||
|
||||
@@ -43,6 +43,26 @@ describe('ReactDOMFiber', () => {
|
||||
expect(container.textContent).toEqual('10');
|
||||
});
|
||||
|
||||
it('should be called a callback argument', () => {
|
||||
// mounting phase
|
||||
let called = false;
|
||||
ReactDOM.render(
|
||||
<div>Foo</div>,
|
||||
container,
|
||||
() => called = true
|
||||
);
|
||||
expect(called).toEqual(true);
|
||||
|
||||
// updating phase
|
||||
called = false;
|
||||
ReactDOM.render(
|
||||
<div>Foo</div>,
|
||||
container,
|
||||
() => called = true
|
||||
);
|
||||
expect(called).toEqual(true);
|
||||
});
|
||||
|
||||
if (ReactDOMFeatureFlags.useFiber) {
|
||||
it('should render a component returning strings directly from render', () => {
|
||||
const Text = ({value}) => value;
|
||||
|
||||
@@ -146,11 +146,11 @@ var ReactNoop = {
|
||||
|
||||
root: rootContainer,
|
||||
|
||||
render(element : ReactElement<any>) {
|
||||
render(element : ReactElement<any>, callback: ?Function) {
|
||||
if (!root) {
|
||||
root = NoopRenderer.mountContainer(element, rootContainer);
|
||||
root = NoopRenderer.mountContainer(element, rootContainer, callback);
|
||||
} else {
|
||||
NoopRenderer.updateContainer(element, root);
|
||||
NoopRenderer.updateContainer(element, root, callback);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -322,6 +322,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
|
||||
attachRef(current, finishedWork, instance);
|
||||
return;
|
||||
}
|
||||
case HostContainer: {
|
||||
const instance = finishedWork.stateNode;
|
||||
if (instance.callbackList) {
|
||||
const { callbackList } = instance;
|
||||
instance.callbackList = null;
|
||||
callCallbacks(callbackList, instance.current.child.stateNode);
|
||||
}
|
||||
}
|
||||
case HostComponent: {
|
||||
const instance : I = finishedWork.stateNode;
|
||||
attachRef(current, finishedWork, instance);
|
||||
|
||||
@@ -20,6 +20,8 @@ import type { PriorityLevel } from 'ReactPriorityLevel';
|
||||
var { createFiberRoot } = require('ReactFiberRoot');
|
||||
var ReactFiberScheduler = require('ReactFiberScheduler');
|
||||
|
||||
var { createUpdateQueue, addCallbackToQueue } = require('ReactFiberUpdateQueue');
|
||||
|
||||
if (__DEV__) {
|
||||
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
|
||||
}
|
||||
@@ -79,9 +81,14 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
|
||||
|
||||
return {
|
||||
|
||||
mountContainer(element : ReactElement<any>, containerInfo : C) : OpaqueNode {
|
||||
mountContainer(element : ReactElement<any>, containerInfo : C, callback: ?Function) : OpaqueNode {
|
||||
const root = createFiberRoot(containerInfo);
|
||||
const container = root.current;
|
||||
if (callback) {
|
||||
const queue = createUpdateQueue(null);
|
||||
addCallbackToQueue(queue, callback);
|
||||
root.callbackList = queue;
|
||||
}
|
||||
// TODO: Use pending work/state instead of props.
|
||||
// TODO: This should not override the pendingWorkPriority if there is
|
||||
// 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>) :
|
||||
return container;
|
||||
},
|
||||
|
||||
updateContainer(element : ReactElement<any>, container : OpaqueNode) : void {
|
||||
updateContainer(element : ReactElement<any>, container : OpaqueNode, callback: ?Function) : void {
|
||||
// TODO: If this is a nested container, this won't be the root.
|
||||
const root : FiberRoot = (container.stateNode : any);
|
||||
if (callback) {
|
||||
const queue = root.callbackList ?
|
||||
root.callbackList :
|
||||
createUpdateQueue(null);
|
||||
addCallbackToQueue(queue, callback);
|
||||
root.callbackList = queue;
|
||||
}
|
||||
// TODO: Use pending work/state instead of props.
|
||||
root.current.pendingProps = element;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
'use strict';
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { UpdateQueue } from 'ReactFiberUpdateQueue';
|
||||
|
||||
const { createHostContainerFiber } = require('ReactFiber');
|
||||
|
||||
@@ -25,6 +26,8 @@ export type FiberRoot = {
|
||||
isScheduled: boolean,
|
||||
// The work schedule is a linked list.
|
||||
nextScheduledRoot: ?FiberRoot,
|
||||
// Linked list of callbacks to call after updates are committed.
|
||||
callbackList: ?UpdateQueue,
|
||||
};
|
||||
|
||||
exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
|
||||
@@ -36,6 +39,7 @@ exports.createFiberRoot = function(containerInfo : any) : FiberRoot {
|
||||
containerInfo: containerInfo,
|
||||
isScheduled: false,
|
||||
nextScheduledRoot: null,
|
||||
callbackList: null,
|
||||
};
|
||||
uninitializedFiber.stateNode = root;
|
||||
return root;
|
||||
|
||||
@@ -173,6 +173,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
|
||||
if (finishedWork.effectTag !== NoEffect) {
|
||||
const current = finishedWork.alternate;
|
||||
commitWork(current, finishedWork);
|
||||
commitLifeCycles(current, finishedWork);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ describe('ReactIncremental', () => {
|
||||
|
||||
it('should render a simple component, in steps if needed', () => {
|
||||
|
||||
var renderCallbackCalled = false;
|
||||
var barCalled = false;
|
||||
function Bar() {
|
||||
barCalled = true;
|
||||
@@ -52,17 +53,20 @@ describe('ReactIncremental', () => {
|
||||
];
|
||||
}
|
||||
|
||||
ReactNoop.render(<Foo />);
|
||||
ReactNoop.render(<Foo />, () => renderCallbackCalled = true);
|
||||
expect(fooCalled).toBe(false);
|
||||
expect(barCalled).toBe(false);
|
||||
expect(renderCallbackCalled).toBe(false);
|
||||
// Do one step of work.
|
||||
ReactNoop.flushDeferredPri(7 + 5);
|
||||
expect(fooCalled).toBe(true);
|
||||
expect(barCalled).toBe(false);
|
||||
expect(renderCallbackCalled).toBe(false);
|
||||
// Do the rest of the work.
|
||||
ReactNoop.flushDeferredPri(50);
|
||||
expect(fooCalled).toBe(true);
|
||||
expect(barCalled).toBe(true);
|
||||
expect(renderCallbackCalled).toBe(true);
|
||||
});
|
||||
|
||||
it('updates a previous render', () => {
|
||||
@@ -98,21 +102,22 @@ describe('ReactIncremental', () => {
|
||||
);
|
||||
}
|
||||
|
||||
ReactNoop.render(<Foo text="foo" />);
|
||||
ReactNoop.render(<Foo text="foo" />, () => ops.push('renderCallbackCalled'));
|
||||
ReactNoop.flush();
|
||||
|
||||
expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer']);
|
||||
expect(ops).toEqual(['Foo', 'Header', 'Content', 'Footer', 'renderCallbackCalled']);
|
||||
|
||||
ops = [];
|
||||
|
||||
ReactNoop.render(<Foo text="bar" />);
|
||||
ReactNoop.render(<Foo text="bar" />, () => ops.push('firstRenderCallbackCalled'));
|
||||
ReactNoop.render(<Foo text="bar" />, () => ops.push('secondRenderCallbackCalled'));
|
||||
ReactNoop.flush();
|
||||
|
||||
// TODO: Test bail out of host components. This is currently unobservable.
|
||||
|
||||
// Since this is an update, it should bail out and reuse the work from
|
||||
// Header and Content.
|
||||
expect(ops).toEqual(['Foo', 'Content']);
|
||||
expect(ops).toEqual(['Foo', 'Content', 'firstRenderCallbackCalled', 'secondRenderCallbackCalled']);
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user