@samitouri / QOS-React / commits / 62d4561910

Host environment side-effects

This creates a new API for processing side-effects on the host environment. During initial reconciliation host instances are created during the time sliced periods. During updates there is an opportunity for the host to prepare something on the instance during the time slicing, and to determine whether there were any changes. The could be thrown away. At the commit phase, these changes are finally committed to the host instance.

Sebastian Markbage committed Jun 30, 2016 at 00:22 UTC 62d4561910aa6c8f07ddd0f40415b70e546b81dc
5 files changed +65 -13
src/renderers/noop/ReactNoop.js
+25 -1
@@ -20,20 +20,44 @@
20 'use strict';
21
22 import type { Fiber } from 'ReactFiber';
23 +import type { HostChildren } from 'ReactFiberReconciler';
24
25 var ReactFiberReconciler = require('ReactFiberReconciler');
26
27 var scheduledHighPriCallback = null;
28 var scheduledLowPriCallback = null;
29
30 +type Props = { };
31 +type Instance = { id: number };
32 +
33 +var instanceCounter = 0;
34 +
35 var NoopRenderer = ReactFiberReconciler({
36
31 - createHostInstance() {
37 + createInstance(type : string, props : Props, children : HostChildren<Instance>) : Instance {
38 + console.log('Create instance #' + instanceCounter);
39 + return {
40 + id: instanceCounter++
41 + };
42 + },
43 +
44 + prepareUpdate(instance : Instance, oldProps : Props, newProps : Props, children : HostChildren<Instance>) : boolean {
45 + console.log('Prepare for update on #' + instance.id);
46 + return true;
47 + },
48
49 + commitUpdate(instance : Instance, oldProps : Props, newProps : Props, children : HostChildren<Instance>) : void {
50 + console.log('Commit update on #' + instance.id);
51 },
52 +
53 + deleteInstance(instance : Instance) : void {
54 + console.log('Delete #' + instance.id);
55 + },
56 +
57 scheduleHighPriCallback(callback) {
58 scheduledHighPriCallback = callback;
59 },
60 +
61 scheduleLowPriCallback(callback) {
62 scheduledLowPriCallback = callback;
63 },
src/renderers/shared/fiber/ReactFiber.js
+1 -1
@@ -48,7 +48,7 @@ type Instance = {
48 type: any,
49
50 // The local state associated with this fiber.
51 - stateNode: ?Object,
51 + stateNode: any,
52
53 // Conceptual aliases
54 // parent : Instance -> return The parent happens to be the same as the
src/renderers/shared/fiber/ReactFiberCommitWork.js
+13 -1
@@ -24,6 +24,8 @@ var {
24
25 module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
26
27 + const commitUpdate = config.commitUpdate;
28 +
29 function commitWork(finishedWork : Fiber) : void {
30 switch (finishedWork.tag) {
31 case ClassComponent:
@@ -33,7 +35,17 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
35 // TODO: Attach children to root container.
36 return;
37 case HostComponent:
36 - console.log('commit updates to host component', finishedWork.type);
38 + if (finishedWork.stateNode == null || !finishedWork.alternate) {
39 + throw new Error('This should only be done during updates.');
40 + }
41 + const children = finishedWork.output;
42 + const newProps = finishedWork.memoizedProps;
43 + // If we have an alternate, that means this is an update and we need to
44 + // schedule a side-effect to do the updates.
45 + const current = finishedWork.alternate;
46 + const oldProps = current.memoizedProps;
47 + const instance : I = finishedWork.stateNode;
48 + commitUpdate(instance, oldProps, newProps, children);
49 return;
50 default:
51 throw new Error('This unit of work tag should not have side-effects.');
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+17 -4
@@ -15,7 +15,6 @@
15 import type { ReactCoroutine } from 'ReactCoroutine';
16 import type { Fiber } from 'ReactFiber';
17 import type { HostConfig } from 'ReactFiberReconciler';
18 -
18 import type { ReifiedYield } from 'ReactReifiedYield';
19
20 var ReactChildFiber = require('ReactChildFiber');
@@ -33,6 +32,9 @@ var {
32
33 module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
34
35 + const createInstance = config.createInstance;
36 + const prepareUpdate = config.prepareUpdate;
37 +
38 function markForPostEffect(workInProgress : Fiber) {
39 // Schedule a side-effect on this fiber, after the children's side-effects.
40 if (workInProgress.lastEffect) {
@@ -120,13 +122,24 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
122 case HostContainer:
123 return null;
124 case HostComponent:
125 + console.log('/host component', workInProgress.type);
126 transferOutput(workInProgress.child, workInProgress);
124 - if (workInProgress.alternate) {
127 + const children = workInProgress.output;
128 + const newProps = workInProgress.memoizedProps;
129 + if (workInProgress.alternate && workInProgress.stateNode != null) {
130 // If we have an alternate, that means this is an update and we need to
131 // schedule a side-effect to do the updates.
127 - markForPostEffect(workInProgress);
132 + const current = workInProgress.alternate;
133 + const oldProps = current.memoizedProps;
134 + const instance : I = workInProgress.stateNode;
135 + if (prepareUpdate(instance, oldProps, newProps, children)) {
136 + // This returns true if there was something to update.
137 + markForPostEffect(workInProgress);
138 + }
139 + } else {
140 + const instance = createInstance(workInProgress.type, newProps, children);
141 + workInProgress.stateNode = instance;
142 }
129 - console.log('/host component', workInProgress.type);
143 return null;
144 case CoroutineComponent:
145 console.log('/coroutine component', workInProgress.pendingProps.handler.name);
src/renderers/shared/fiber/ReactFiberReconciler.js
+9 -6
@@ -22,18 +22,21 @@ var {
22 LowPriority,
23 } = require('ReactPriorityLevel');
24
25 -type ReactHostElement<T, P> = {
26 - type: T,
27 - props: P
28 -};
29 -
25 type Deadline = {
26 timeRemaining : () => number
27 };
28
29 +type HostChildNode<I> = { output: HostChildren<I>, sibling: ?HostChildNode<I> };
30 +
31 +export type HostChildren<I> = null | void | I | HostChildNode<I>;
32 +
33 export type HostConfig<T, P, I> = {
34
36 - createHostInstance(element : ReactHostElement<T, P>) : I,
35 + createInstance(type : T, props : P, children : HostChildren<I>) : I,
36 + prepareUpdate(instance : I, oldProps : P, newProps : P, children : HostChildren<I>) : bool,
37 + commitUpdate(instance : I, oldProps : P, newProps : P, children : HostChildren<I>) : void,
38 + deleteInstance(instance : I) : void,
39 +
40 scheduleHighPriCallback(callback : () => void) : void,
41 scheduleLowPriCallback(callback : (deadline : Deadline) => void) : void
42