react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / addons / transitions / ReactTransitionGroup.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactTransitionGroup9*/1011"use strict";1213var React = require('React');14var ReactTransitionChildMapping = require('ReactTransitionChildMapping');1516var assign = require('Object.assign');17var cloneWithProps = require('cloneWithProps');18var emptyFunction = require('emptyFunction');1920var ReactTransitionGroup = React.createClass({21displayName: 'ReactTransitionGroup',2223propTypes: {24component: React.PropTypes.any,25childFactory: React.PropTypes.func26},2728getDefaultProps: function() {29return {30component: 'span',31childFactory: emptyFunction.thatReturnsArgument32};33},3435getInitialState: function() {36return {37children: ReactTransitionChildMapping.getChildMapping(this.props.children)38};39},4041componentWillReceiveProps: function(nextProps) {42var nextChildMapping = ReactTransitionChildMapping.getChildMapping(43nextProps.children44);45var prevChildMapping = this.state.children;4647this.setState({48children: ReactTransitionChildMapping.mergeChildMappings(49prevChildMapping,50nextChildMapping51)52});5354var key;5556for (key in nextChildMapping) {57var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);58if (nextChildMapping[key] && !hasPrev &&59!this.currentlyTransitioningKeys[key]) {60this.keysToEnter.push(key);61}62}6364for (key in prevChildMapping) {65var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);66if (prevChildMapping[key] && !hasNext &&67!this.currentlyTransitioningKeys[key]) {68this.keysToLeave.push(key);69}70}7172// If we want to someday check for reordering, we could do it here.73},7475componentWillMount: function() {76this.currentlyTransitioningKeys = {};77this.keysToEnter = [];78this.keysToLeave = [];79},8081componentDidUpdate: function() {82var keysToEnter = this.keysToEnter;83this.keysToEnter = [];84keysToEnter.forEach(this.performEnter);8586var keysToLeave = this.keysToLeave;87this.keysToLeave = [];88keysToLeave.forEach(this.performLeave);89},9091performEnter: function(key) {92this.currentlyTransitioningKeys[key] = true;9394var component = this.refs[key];9596if (component.componentWillEnter) {97component.componentWillEnter(98this._handleDoneEntering.bind(this, key)99);100} else {101this._handleDoneEntering(key);102}103},104105_handleDoneEntering: function(key) {106var component = this.refs[key];107if (component.componentDidEnter) {108component.componentDidEnter();109}110111delete this.currentlyTransitioningKeys[key];112113var currentChildMapping = ReactTransitionChildMapping.getChildMapping(114this.props.children115);116117if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {118// This was removed before it had fully entered. Remove it.119this.performLeave(key);120}121},122123performLeave: function(key) {124this.currentlyTransitioningKeys[key] = true;125126var component = this.refs[key];127if (component.componentWillLeave) {128component.componentWillLeave(this._handleDoneLeaving.bind(this, key));129} else {130// Note that this is somewhat dangerous b/c it calls setState()131// again, effectively mutating the component before all the work132// is done.133this._handleDoneLeaving(key);134}135},136137_handleDoneLeaving: function(key) {138var component = this.refs[key];139140if (component.componentDidLeave) {141component.componentDidLeave();142}143144delete this.currentlyTransitioningKeys[key];145146var currentChildMapping = ReactTransitionChildMapping.getChildMapping(147this.props.children148);149150if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {151// This entered again before it fully left. Add it again.152this.performEnter(key);153} else {154var newChildren = assign({}, this.state.children);155delete newChildren[key];156this.setState({children: newChildren});157}158},159160render: function() {161// TODO: we could get rid of the need for the wrapper node162// by cloning a single child163var childrenToRender = {};164for (var key in this.state.children) {165var child = this.state.children[key];166if (child) {167// You may need to apply reactive updates to a child as it is leaving.168// The normal React way to do it won't work since the child will have169// already been removed. In case you need this behavior you can provide170// a childFactory function to wrap every child, even the ones that are171// leaving.172childrenToRender[key] = cloneWithProps(173this.props.childFactory(child),174{ref: key}175);176}177}178return React.createElement(179this.props.component,180this.props,181childrenToRender182);183}184});185186module.exports = ReactTransitionGroup;187188189