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.
This commit is contained in:
Sebastian Markbage
2016-06-30 14:29:17 -07:00
parent a4b8bebe18
commit 62d4561910
5 changed files with 66 additions and 14 deletions
+26 -2
View File
@@ -20,20 +20,44 @@
'use strict';
import type { Fiber } from 'ReactFiber';
import type { HostChildren } from 'ReactFiberReconciler';
var ReactFiberReconciler = require('ReactFiberReconciler');
var scheduledHighPriCallback = null;
var scheduledLowPriCallback = null;
type Props = { };
type Instance = { id: number };
var instanceCounter = 0;
var NoopRenderer = ReactFiberReconciler({
createHostInstance() {
createInstance(type : string, props : Props, children : HostChildren<Instance>) : Instance {
console.log('Create instance #' + instanceCounter);
return {
id: instanceCounter++
};
},
prepareUpdate(instance : Instance, oldProps : Props, newProps : Props, children : HostChildren<Instance>) : boolean {
console.log('Prepare for update on #' + instance.id);
return true;
},
commitUpdate(instance : Instance, oldProps : Props, newProps : Props, children : HostChildren<Instance>) : void {
console.log('Commit update on #' + instance.id);
},
deleteInstance(instance : Instance) : void {
console.log('Delete #' + instance.id);
},
scheduleHighPriCallback(callback) {
scheduledHighPriCallback = callback;
},
scheduleLowPriCallback(callback) {
scheduledLowPriCallback = callback;
},
+1 -1
View File
@@ -48,7 +48,7 @@ type Instance = {
type: any,
// The local state associated with this fiber.
stateNode: ?Object,
stateNode: any,
// Conceptual aliases
// parent : Instance -> return The parent happens to be the same as the
@@ -24,6 +24,8 @@ var {
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
const commitUpdate = config.commitUpdate;
function commitWork(finishedWork : Fiber) : void {
switch (finishedWork.tag) {
case ClassComponent:
@@ -33,7 +35,17 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
// TODO: Attach children to root container.
return;
case HostComponent:
console.log('commit updates to host component', finishedWork.type);
if (finishedWork.stateNode == null || !finishedWork.alternate) {
throw new Error('This should only be done during updates.');
}
const children = finishedWork.output;
const newProps = finishedWork.memoizedProps;
// If we have an alternate, that means this is an update and we need to
// schedule a side-effect to do the updates.
const current = finishedWork.alternate;
const oldProps = current.memoizedProps;
const instance : I = finishedWork.stateNode;
commitUpdate(instance, oldProps, newProps, children);
return;
default:
throw new Error('This unit of work tag should not have side-effects.');
@@ -15,7 +15,6 @@
import type { ReactCoroutine } from 'ReactCoroutine';
import type { Fiber } from 'ReactFiber';
import type { HostConfig } from 'ReactFiberReconciler';
import type { ReifiedYield } from 'ReactReifiedYield';
var ReactChildFiber = require('ReactChildFiber');
@@ -33,6 +32,9 @@ var {
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
const createInstance = config.createInstance;
const prepareUpdate = config.prepareUpdate;
function markForPostEffect(workInProgress : Fiber) {
// Schedule a side-effect on this fiber, after the children's side-effects.
if (workInProgress.lastEffect) {
@@ -120,13 +122,24 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
case HostContainer:
return null;
case HostComponent:
console.log('/host component', workInProgress.type);
transferOutput(workInProgress.child, workInProgress);
if (workInProgress.alternate) {
const children = workInProgress.output;
const newProps = workInProgress.memoizedProps;
if (workInProgress.alternate && workInProgress.stateNode != null) {
// If we have an alternate, that means this is an update and we need to
// schedule a side-effect to do the updates.
markForPostEffect(workInProgress);
const current = workInProgress.alternate;
const oldProps = current.memoizedProps;
const instance : I = workInProgress.stateNode;
if (prepareUpdate(instance, oldProps, newProps, children)) {
// This returns true if there was something to update.
markForPostEffect(workInProgress);
}
} else {
const instance = createInstance(workInProgress.type, newProps, children);
workInProgress.stateNode = instance;
}
console.log('/host component', workInProgress.type);
return null;
case CoroutineComponent:
console.log('/coroutine component', workInProgress.pendingProps.handler.name);
@@ -22,18 +22,21 @@ var {
LowPriority,
} = require('ReactPriorityLevel');
type ReactHostElement<T, P> = {
type: T,
props: P
};
type Deadline = {
timeRemaining : () => number
};
type HostChildNode<I> = { output: HostChildren<I>, sibling: ?HostChildNode<I> };
export type HostChildren<I> = null | void | I | HostChildNode<I>;
export type HostConfig<T, P, I> = {
createHostInstance(element : ReactHostElement<T, P>) : I,
createInstance(type : T, props : P, children : HostChildren<I>) : I,
prepareUpdate(instance : I, oldProps : P, newProps : P, children : HostChildren<I>) : bool,
commitUpdate(instance : I, oldProps : P, newProps : P, children : HostChildren<I>) : void,
deleteInstance(instance : I) : void,
scheduleHighPriCallback(callback : () => void) : void,
scheduleLowPriCallback(callback : (deadline : Deadline) => void) : void