New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fiber] Child Reconciler + New Coroutines Primitive #6859
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/** | ||
* Copyright 2013-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* @providesModule ReactChildFiber | ||
* @flow | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import type { ReactCoroutine, ReactYield } from 'ReactCoroutine'; | ||
import type { Fiber } from 'ReactFiber'; | ||
|
||
import type { ReactNodeList } from 'ReactTypes'; | ||
|
||
var { | ||
REACT_ELEMENT_TYPE, | ||
} = require('ReactElement'); | ||
var { | ||
REACT_COROUTINE_TYPE, | ||
REACT_YIELD_TYPE, | ||
} = require('ReactCoroutine'); | ||
|
||
var ReactFiber = require('ReactFiber'); | ||
var ReactReifiedYield = require('ReactReifiedYield'); | ||
|
||
function createSubsequentChild(parent : Fiber, previousSibling : Fiber, newChildren) : Fiber { | ||
if (typeof newChildren !== 'object' || newChildren === null) { | ||
return previousSibling; | ||
} | ||
|
||
switch (newChildren.$$typeof) { | ||
case REACT_ELEMENT_TYPE: { | ||
const element = (newChildren : ReactElement); | ||
const child = ReactFiber.createFiberFromElement(element); | ||
previousSibling.sibling = child; | ||
child.parent = parent; | ||
return child; | ||
} | ||
|
||
case REACT_COROUTINE_TYPE: { | ||
const coroutine = (newChildren : ReactCoroutine); | ||
const child = ReactFiber.createFiberFromCoroutine(coroutine); | ||
previousSibling.sibling = child; | ||
child.parent = parent; | ||
return child; | ||
} | ||
|
||
case REACT_YIELD_TYPE: { | ||
const yieldNode = (newChildren : ReactYield); | ||
const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode); | ||
const child = ReactFiber.createFiberFromYield(yieldNode); | ||
child.output = reifiedYield; | ||
previousSibling.sibling = child; | ||
child.parent = parent; | ||
return child; | ||
} | ||
} | ||
|
||
if (Array.isArray(newChildren)) { | ||
let prev : Fiber = previousSibling; | ||
for (var i = 0; i < newChildren.length; i++) { | ||
prev = createSubsequentChild(parent, prev, newChildren[i]); | ||
} | ||
return prev; | ||
} else { | ||
console.log('Unknown child', newChildren); | ||
return previousSibling; | ||
} | ||
} | ||
|
||
function createFirstChild(parent, newChildren) { | ||
if (typeof newChildren !== 'object' || newChildren === null) { | ||
return null; | ||
} | ||
|
||
switch (newChildren.$$typeof) { | ||
case REACT_ELEMENT_TYPE: { | ||
const element = (newChildren : ReactElement); | ||
const child = ReactFiber.createFiberFromElement(element); | ||
child.parent = parent; | ||
return child; | ||
} | ||
|
||
case REACT_COROUTINE_TYPE: { | ||
const coroutine = (newChildren : ReactCoroutine); | ||
const child = ReactFiber.createFiberFromCoroutine(coroutine); | ||
child.parent = parent; | ||
return child; | ||
} | ||
|
||
case REACT_YIELD_TYPE: { | ||
// A yield results in a fragment fiber whose output is the continuation. | ||
// TODO: When there is only a single child, we can optimize this to avoid | ||
// the fragment. | ||
const yieldNode = (newChildren : ReactYield); | ||
const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode); | ||
const child = ReactFiber.createFiberFromYield(yieldNode); | ||
child.output = reifiedYield; | ||
child.parent = parent; | ||
return child; | ||
} | ||
} | ||
|
||
if (Array.isArray(newChildren)) { | ||
var first : ?Fiber = null; | ||
var prev : ?Fiber = null; | ||
for (var i = 0; i < newChildren.length; i++) { | ||
if (prev == null) { | ||
prev = createFirstChild(parent, newChildren[i]); | ||
first = prev; | ||
} else { | ||
prev = createSubsequentChild(parent, prev, newChildren[i]); | ||
} | ||
} | ||
return first; | ||
} else { | ||
console.log('Unknown child', newChildren); | ||
return null; | ||
} | ||
} | ||
|
||
exports.reconcileChildFibers = function(parent : Fiber, firstChild : ?Fiber, newChildren : ReactNodeList) : ?Fiber { | ||
return createFirstChild(parent, newChildren); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,31 +12,47 @@ | |
|
||
'use strict'; | ||
|
||
type StateNode = {}; | ||
type EffectHandler = () => void; | ||
type EffectTag = number; | ||
var ReactTypesOfWork = require('ReactTypesOfWork'); | ||
var { | ||
IndeterminateComponent, | ||
ClassComponent, | ||
HostComponent, | ||
CoroutineComponent, | ||
YieldComponent, | ||
} = ReactTypesOfWork; | ||
|
||
var ReactElement = require('ReactElement'); | ||
|
||
import type { ReactCoroutine, ReactYield } from 'ReactCoroutine'; | ||
|
||
export type Fiber = { | ||
|
||
// Tag identifying the type of fiber. | ||
tag: number, | ||
|
||
parent: ?Fiber, | ||
// Singly Linked List Tree Structure. | ||
parent: ?Fiber, // Consider a regenerated temporary parent stack instead. | ||
child: ?Fiber, | ||
sibling: ?Fiber, | ||
|
||
input: ?Object, | ||
output: ?Object, | ||
// Input is the data coming into process this fiber. Arguments. | ||
input: any, // This type will be more specific once we overload the tag. | ||
// Output is the return value of this fiber, or a linked list of return values | ||
// if this returns multiple values. Such as a fragment. | ||
output: any, // This type will be more specific once we overload the tag. | ||
|
||
handler: EffectHandler, | ||
handlerTag: EffectTag, | ||
// Used by multi-stage coroutines. | ||
stage: number, // Consider reusing the tag field instead. | ||
|
||
// This will be used to quickly determine if a subtree has no pending changes. | ||
hasPendingChanges: bool, | ||
|
||
stateNode: StateNode, | ||
// The local state associated with this fiber. | ||
stateNode: ?Object, | ||
|
||
}; | ||
|
||
module.exports = function(tag : number) : Fiber { | ||
var createFiber = function(tag : number) : Fiber { | ||
return { | ||
|
||
tag: tag, | ||
|
@@ -48,12 +64,53 @@ module.exports = function(tag : number) : Fiber { | |
input: null, | ||
output: null, | ||
|
||
handler: function() {}, | ||
handlerTag: 0, | ||
stage: 0, | ||
|
||
hasPendingChanges: true, | ||
|
||
stateNode: {}, | ||
stateNode: null, | ||
|
||
}; | ||
}; | ||
|
||
function shouldConstruct(Component) { | ||
return !!(Component.prototype && Component.prototype.isReactComponent); | ||
} | ||
|
||
exports.createFiberFromElement = function(element : ReactElement) { | ||
const fiber = exports.createFiberFromElementType(element.type); | ||
if (typeof element.type === 'object') { | ||
// Hacky McHack | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
element = ReactElement(fiber.input, null, element.ref, null, null, null, element.props); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this case for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, I get it now. |
||
} | ||
fiber.input = element; | ||
return fiber; | ||
}; | ||
|
||
exports.createFiberFromElementType = function(type : mixed) { | ||
let fiber; | ||
if (typeof type === 'function') { | ||
fiber = shouldConstruct(type) ? | ||
createFiber(ClassComponent) : | ||
createFiber(IndeterminateComponent); | ||
} else if (typeof type === 'string') { | ||
fiber = createFiber(HostComponent); | ||
} else if (typeof type === 'object' && type !== null) { | ||
// Currently assumed to be a continuation and therefore is a fiber already. | ||
fiber = type; | ||
} else { | ||
throw new Error('Unknown component type: ' + typeof type); | ||
} | ||
return fiber; | ||
}; | ||
|
||
exports.createFiberFromCoroutine = function(coroutine : ReactCoroutine) { | ||
const fiber = createFiber(CoroutineComponent); | ||
fiber.input = coroutine; | ||
return fiber; | ||
}; | ||
|
||
exports.createFiberFromYield = function(yieldNode : ReactYield) { | ||
const fiber = createFiber(YieldComponent); | ||
return fiber; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I guess this is a minor-version change as it allows objects as element types. Should we maybe do a backport of this and exclude this change? We might also be able to get away with not cherry-picking this at all…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only changes the warning behavior, but it would be nice if we could be a little more strict here. Forgetting to export anything from a CommonJS module lands you an empty object here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add and
Object.keys(type).length
check.Regardless we probably don't want this part in 15 since it will change the warning behavior (at least before if you had the case you talk about you know something is about to go wrong).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to be throwing later on if you tried to render this anywhere before. It is also going to be throwing through out version 15 so even if you try a later version and then downgrades, it'll still not warn. So I think this is pretty safe.
We can be more specific once we know a bit more about the data structures that will be allowed here. I suspect "module components" might end up here if we do those. As well as yields.