2017-05-03 15:35:00 +02:00
/ * *
* Copyright 2014 - 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 .
*
* /
'use strict' ;
var ReactReconciler = require ( './ReactReconciler' ) ;
var instantiateReactComponent = require ( './instantiateReactComponent' ) ;
var KeyEscapeUtils = require ( './KeyEscapeUtils' ) ;
var shouldUpdateReactComponent = require ( './shouldUpdateReactComponent' ) ;
var traverseAllChildren = require ( './traverseAllChildren' ) ;
var warning = require ( 'fbjs/lib/warning' ) ;
var ReactComponentTreeHook ;
if ( typeof process !== 'undefined' && process . env && process . env . NODE _ENV === 'test' ) {
// Temporary hack.
// Inline requires don't work well with Jest:
// https://github.com/facebook/react/issues/7240
// Remove the inline requires when we don't need them anymore:
// https://github.com/facebook/react/pull/7178
ReactComponentTreeHook = require ( 'react/lib/ReactComponentTreeHook' ) ;
}
function instantiateChild ( childInstances , child , name , selfDebugID ) {
// We found a component instance.
var keyUnique = childInstances [ name ] === undefined ;
if ( process . env . NODE _ENV !== 'production' ) {
if ( ! ReactComponentTreeHook ) {
ReactComponentTreeHook = require ( 'react/lib/ReactComponentTreeHook' ) ;
}
if ( ! keyUnique ) {
process . env . NODE _ENV !== 'production' ? warning ( false , 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s' , KeyEscapeUtils . unescape ( name ) , ReactComponentTreeHook . getStackAddendumByID ( selfDebugID ) ) : void 0 ;
}
}
if ( child != null && keyUnique ) {
childInstances [ name ] = instantiateReactComponent ( child , true ) ;
}
}
/ * *
* ReactChildReconciler provides helpers for initializing or updating a set of
* children . Its output is suitable for passing it onto ReactMultiChild which
* does diffed reordering and insertion .
* /
var ReactChildReconciler = {
/ * *
* Generates a "mount image" for each of the supplied children . In the case
* of ` ReactDOMComponent ` , a mount image is a string of markup .
*
* @ param { ? object } nestedChildNodes Nested child maps .
* @ return { ? object } A set of child instances .
* @ internal
* /
2017-08-14 05:01:11 +02:00
instantiateChildren : function ( nestedChildNodes , transaction , context , selfDebugID ) // 0 in production and for roots
{
2017-05-03 15:35:00 +02:00
if ( nestedChildNodes == null ) {
return null ;
}
var childInstances = { } ;
if ( process . env . NODE _ENV !== 'production' ) {
traverseAllChildren ( nestedChildNodes , function ( childInsts , child , name ) {
return instantiateChild ( childInsts , child , name , selfDebugID ) ;
} , childInstances ) ;
} else {
traverseAllChildren ( nestedChildNodes , instantiateChild , childInstances ) ;
}
return childInstances ;
} ,
/ * *
* Updates the rendered children and returns a new set of children .
*
* @ param { ? object } prevChildren Previously initialized set of children .
* @ param { ? object } nextChildren Flat child element maps .
* @ param { ReactReconcileTransaction } transaction
* @ param { object } context
* @ return { ? object } A new set of child instances .
* @ internal
* /
2017-08-14 05:01:11 +02:00
updateChildren : function ( prevChildren , nextChildren , mountImages , removedNodes , transaction , hostParent , hostContainerInfo , context , selfDebugID ) // 0 in production and for roots
{
2017-05-03 15:35:00 +02:00
// We currently don't have a way to track moves here but if we use iterators
// instead of for..in we can zip the iterators and check if an item has
// moved.
// TODO: If nothing has changed, return the prevChildren object so that we
// can quickly bailout if nothing has changed.
if ( ! nextChildren && ! prevChildren ) {
return ;
}
var name ;
var prevChild ;
for ( name in nextChildren ) {
if ( ! nextChildren . hasOwnProperty ( name ) ) {
continue ;
}
prevChild = prevChildren && prevChildren [ name ] ;
var prevElement = prevChild && prevChild . _currentElement ;
var nextElement = nextChildren [ name ] ;
if ( prevChild != null && shouldUpdateReactComponent ( prevElement , nextElement ) ) {
ReactReconciler . receiveComponent ( prevChild , nextElement , transaction , context ) ;
nextChildren [ name ] = prevChild ;
} else {
if ( prevChild ) {
removedNodes [ name ] = ReactReconciler . getHostNode ( prevChild ) ;
ReactReconciler . unmountComponent ( prevChild , false ) ;
}
// The child must be instantiated before it's mounted.
var nextChildInstance = instantiateReactComponent ( nextElement , true ) ;
nextChildren [ name ] = nextChildInstance ;
// Creating mount image now ensures refs are resolved in right order
// (see https://github.com/facebook/react/pull/7101 for explanation).
var nextChildMountImage = ReactReconciler . mountComponent ( nextChildInstance , transaction , hostParent , hostContainerInfo , context , selfDebugID ) ;
mountImages . push ( nextChildMountImage ) ;
}
}
// Unmount children that are no longer present.
for ( name in prevChildren ) {
if ( prevChildren . hasOwnProperty ( name ) && ! ( nextChildren && nextChildren . hasOwnProperty ( name ) ) ) {
prevChild = prevChildren [ name ] ;
removedNodes [ name ] = ReactReconciler . getHostNode ( prevChild ) ;
ReactReconciler . unmountComponent ( prevChild , false ) ;
}
}
} ,
/ * *
* Unmounts all rendered children . This should be used to clean up children
* when this component is unmounted .
*
* @ param { ? object } renderedChildren Previously initialized set of children .
* @ internal
* /
unmountChildren : function ( renderedChildren , safely ) {
for ( var name in renderedChildren ) {
if ( renderedChildren . hasOwnProperty ( name ) ) {
var renderedChild = renderedChildren [ name ] ;
ReactReconciler . unmountComponent ( renderedChild , safely ) ;
}
}
}
} ;
module . exports = ReactChildReconciler ;