this is a very simple react app with just App component and Child component. I am passing objects individually to the Child component using .map function in the parent. Each object is received in child(console), but I cannot get the object fields to output/display on browser or console, it just showing as a blank screen, also does not show any errors.
each person is recieved but individual fields cannot be from access like {person.Name} in the child component, I have also tried destructing the prop, still does not work
I have looked at other questions
Passing data from Parent Component to Child Component
How to pass Array of Objects from Parent Component to Child Component in react
before asking this question plus I have searched a lot online for solution
PARENT COMPONENT
import React from 'react'
import './App.css';
import Child from './Child'
function App() {
let persons = [
{
Name: 'Bill Gates',
id: 432,
Place: 'USA'
},
{
Name: 'Danny Archer',
id: 34,
Place: 'Blood Diamond'
},
{
Name: 'Iphone X',
id: 2018,
Place: 'USA'
},
{
Name: 'React App',
id: 111,
Place: 'Front End Programming'
},
{
Name: 'Honda CBR 600RR',
id: 100,
Place: 'Bike'
},
]
console.log(persons);
return (
<div className="App">
{
persons.map(person=>(<Child person = {person} key={person.id}/>))
}
</div>
);
}
export default App;
CHILD COMPONENT
import React from 'react'
function Child(person) {
// console.log(person)
return (
<div >
<h1>{person.Name}</h1>
</div>
)
}
export default Child
Props is an object, not the exact value you were expecting
import React from 'react'
/// change this line
function Child({person}) {
// console.log(person)
return (
<div >
<h1>{person.Name}</h1>
</div>
)
}
export default Child
In your code, function Child(person)
this person, is an object containing the props you pass like, for example Child(person={person} someData={someData})
so, person param in function will be
{
person: person,
someData: someData
}
If you console.log person in child component you will get:
person: { person: { name, id, place }}
because the first parameter will get all of the properties from the parent.
So instead of doing this --> function Child(person), you can do something like this --> function Child({person}). So this will destructure your prop object & you will get your person object!
Related
I'm learning react from the book full stack react. In one of the examples votingapp where you have products and a button to vote for the product. That button supposes to increase the number of votes for that product and I store the votes number in a component Parent's state and display it in a child component. That voting feature is not working.
I created Parent component where it displays child component that present product description, id, color and votes (the number of votes the product received)
import React, { Component } from 'react';
import './App.css';
import Product from "./Product";
var products = [
{
id: 1,
name: "one",
color: "blue",
votes:0
},
{
id: 2,
name: "two",
color: "green",
votes : 0
},
{
id: 3,
name: "three",
color: "Red",
votes : 1
}
];
class App extends Component {
//this function will be passed to child component so the child can pass any data needed back to the parent using function argument.
handleProductUpVote(productId) {
console.log("Product #" + " " +productId + " was upvoted")
};
render() {
const productComponents = products.map((product) => {
return <Product
key={"product-" + product.id}
id={product.id}
name={product.name}
color={product.color}
votes={product.votes}
/*this is how we pass the function from parent to the child as a prop*/
onVote={this.handleProductUpVote}
/>
});
return (
<div className="App">
{productComponents}
</div>
);
}
}
export default App;
and here is my child component where it renders the product details
import React, { Component } from 'react';
class Product extends Component {
constructor(props) {
super(props);
this.handleUpVote = this.handleUpVote.bind(this);
}
//using the function passed from parent in a child. We pass any data needed back to parent component using parent's function arugments
// invoke this function using onClick event inside the button
handleUpVote() {
this.props.onVote(this.props.id);
};
render() {
return (
<div>
<p> name: {this.props.name} </p>
<p> MYcolor: {this.props.color} </p>
{/*invoke the function using onClick so it will invoke handleUpVote that will update the prop and pass the data back to parent*/}
<button onClick={this.handleUpVote}> Upvote Me </button>
<p>{this.props.votes}</p>
<hr></hr>
</div>
)
}
};
export default Product;
this is working and I log to the console the message when I hit the button "Upvoteme"
But When I'm trying to move the setup to use state. It doesn't work Here is the parent component with the state and setState. When I click on the vote button nothing happens to the vote count.
import React, { Component } from 'react';
import './App.css';
import Child from "./Child";
var productseed = [
{
id: 1,
name: "one",
color: "blue",
votes: 0
},
{
id: 2,
name: "two",
color: "green",
votes : 0
},
{
id: 3,
name: "three",
color: "Red",
votes : 1
}
];
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
this.handleProductUpVote = this.handleProductUpVote.bind(this);
}
componentDidMount() {
this.setState ({ products: productseed });
}
//this function will be passed to child component so the child can pass any data needed back to the parent using function argument.
handleProductUpVote(productId) {
// updating the vote in state
const nextProducts = this.state.products.map((product) => {
if (product.id === productId) {
return Object.assign({}, product, {
votes: product.votes + 1,
});
} else {
return product
}
});
this.setState({
products: nextProducts,
});
}
render() {
const productComponents = productseed.map((product) => {
return <Child
key={"product-" + product.id}
id={product.id}
name={product.name}
color={product.color}
votes={product.votes}
/*this is how we pass the function from parent to the child as a prop*/
onVote={this.handleProductUpVote}
/>
});
return (
<div className="App">
parent
{productComponents}
</div>
);
}
}
export default Parent;
The line below suppose to point to the products in the state but when I highlight it, it doesn't highlight the products in the this.state.
The consolelog is logging the message to my developer console. This is the issue, products in this.setState isn't pointing to the this.state.products and therefore not updating the state.
componentDidMount() {
this.setState({ products: productseed });
console.log("this products not pointing to the this.state.products, why?")
}
I read every question on stackoverflow related to setState not working and yet I have the same problem. If you an expert with react and able to take a look at this and figure out where is the issue, I would be so grateful. I'm unable to figure out when I assigned the this.setState({products: productseed}) and it doesn't update the state. I spent almost the past 4 hours reading and researching, please help.
Your problem lies in the render method of your Parent component. You're iterating over the productseeds array instead of your state. Since you're updating the state and not the seed array react sees no reason to rerender anything and therefore nothing changes.
So if you change that line from
const productComponents = productseed.map((product) => {...
to
const productComponents = this.state.products.map((product) => {...
you should be fine.
Moreover about your:
The line below suppose to point to the products in the state but when I highlight it, it doesn't highlight the products in the this.state.
This is just something related to the IDE you're using and nothing specific about react. You're passing an object with attributes and most IDEs (or all (?)) don't connect the combination with this.setState to the state object.
I'm trying to pass props of looped component(using array.map()) to other component, made as follows:
{this.posts.map((item, index) => {
return (
<Post
item={item}
key = {index}
/>
);
})
}
So there are many 'Post' sibling components being rendered, but each with different items(data) and keys. Now I wish to send the props(to share a state between the two components) of one specific Post component to another specific sibling Post component. That is, I wish to select a specific Post component (maybe with the key value? and send a state only to another specific post component).
How would I achieve this?
Thank you.
I would recommend binding a function that would update this.posts. That way your components will all update without having to take on potentially entangled states.
The idea is the function updatePosts will take a target index (key) child component to update and pass it the new state. In the code below, if you triggered updatePosts({firstName: 'Harry', lastName: 'Styles'}, 1), then the state would change from the first element being 'Foo Bar' to being 'Harry Styles', and the child components will rerender.
import React from 'react';
class PostMaster extends React.Component {
constructor(props) {
super(props);
this.state = {
//the array you use to render child components will be managed in a state like this
data: [
{
firstName: 'Foo',
lastName: 'Bar'
},
{
firstName: 'James',
lastName: 'Bond'
},
{
firstName: 'Harry',
lastName: 'Potter'
}
]
};
this.updatePosts = this.updatePosts.bind(this);
};
/**
* stateData (object) the data you want to go in a given index
* index (int) the child component you would like to update
/
updatePosts(stateData, index) {
let newData = this.state.data;
newData[index] = stateData;
this.setState({data: newData});
//you can always push instead of reassigning
};
render() {
return(
<div>
{this.state.data((item, index) => {
return (
<Post
item={item}
key={index}
update={this.updatePosts}
/>
);
})}
</div>
)
}
}
I was reading React docs, about Components and props here. One thing got me confused. In this part of the code, where we pass the props to Avatar Component:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
And where the comment object looks like this:
const comment = {
date: new Date(),
text: 'I hope you enjoy learning React!',
author: {
name: 'Hello Kitty',
avatarUrl: 'http://placekitten.com/g/64/64',
},
};
Since we are already passing only the author property of the object, why do we then need to access author.name and avatarUrl like this in Avatar component:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
And, not like this, that would make more sense in Vue.js, where I am coming from:
function Avatar(props) {
console.log(props);
return (
<img className="Avatar"
src={props.avatarUrl}
alt={props.name}
/>
);
}
I understand why you are confused. Note that each component (parent or child) have a different props object from each other. These are totally unrelated and may have a different set of properties.
So, by passing a value down as prop to a child component, you're not redefining that child's props object (to author, for example), instead, you're almost like appending a new value to it, which was initially empty.
For example,
// Parent props is: { author, posts }
const Parent = (props) => {
return (
/* Child props is a completely different -> object,
** now that nothing is being passed, an empty object -> {}
*/
<Child />
);
};
Now, if parent starts passing a new prop to Child, the Child props now holds the new value within its own props object:
// Parent props is: { author, posts }
const Parent = (props) => {
return (
/* Child props is a completely different object,
** now Child.props { author: Parent.props.author, age: "12" } */
<Child author={props.author} age="12" />
);
};
In essence, each component receives a different unique props object that is set in the initial phase of the component life.
Additionally I'd recommend you to use the object destructuring ES6 feature to save time from typing 'props.author':
const Child = ({ author, age, smoker }) => {
return <h2>Hey ${author.name}</h2>
};
Since props is the first argument passed to the Child component function, by setting it as an object in the signature, we're able to extract the specific values we need.
This is how it would look if we're on a class component, right within the render method, but before returning of the React element. We're initialising const, and assigning this.props, in order to destructure the object and create a local identifier.
class MyComponent extends React.Component {
render() {
const { author, age, smoker } = this.props;
return (
<h2>Hey {author.name}</h2>
);
}
}
If you wish to know more about components, props, state and general life cycle here's a good resource this.
When you pass props to a component you simply access them by the property name the parent pass a value to, because you can pass multiple props this is needed.
To be able to access the props like in your last snippet you can use the object spread :
<Avatar {...props.author} />
Same as :
<Avatar avatarUrl={props.author.avatarUrl} name={props.author.name}/>
You are passing as user prop:
{
name: 'Hello Kitty',
avatarUrl: 'http://placekitten.com/g/64/64',
}
In the Child it will be:
props: {
user: {
name: 'Hello Kitty',
avatarUrl: 'http://placekitten.com/g/64/64',
}
}
To access it:
props.user.avatarUrl
props.user.name
Just remember that props is an object, you are basically taking the value of author, setting it to user and attaching it to props.
I've run into an interesting problem. I have a parent component that has an array of objects that gets passed to a child component that is a TreeView, meaning it is recursive. I'm passing a function, and a couple of other props to the child, along with the array of objects that is handled recursively by the child. When logging the props in the render function of the child, on the first render all the props are there, but as the recursive function moves through each object in the array, it 'loses' all the other props that are not being handled recursively.
When the component first renders the props object is: prop1, prop2, arrayOfObjects
As it re-renders as recursion is happening, the props object in the child becomes: arrayOfObjects.
prop1, and prop2 have disappeared.
The end result is that I'm not able to call a function in the parent from the child, so I cannot update the state depending on which node in the tree is clicked. I'm not using redux, because this is a style guide - separate from our production app, that is meant to be for devs only, and simple so if possible I'd like to handle all the state from within the components.
There is one other issue - The array of objects is the folder structure of files in our styleguide, and I need to be able to click on a name in the list, and update the view with the contents of that file. This works fine when the file does not have any children, but when there are child nodes, if I click on the parent, the child is clicked. I've tried e.stopPropagation(), e.preventDefault() etc. but have not had any luck. Thanks in advance.
Parent:
import React, {Component} from 'react'
import StyleGuideStructure from '../../styleguide_structure.json'
import StyleTree from './style_tree'
class StyleGuide extends Component {
constructor(props) {
super(props)
let tree = StyleGuideStructure
this.state = {
tree: tree
}
This is the function I'd like to call from the child
setVisibleSection(nodeTitle) {
this.setState({
section: nodeTitle
})
}
render() {
return(
<TreeNode
className="class-name-here"
setVisibleSection={this.setVisibleSection.bind(this)}
node={this.state.tree}
/>
)
}
}
export default StyleGuide
This is essentially what I have in the child, as a fiddle here:
https://jsfiddle.net/ssorallen/XX8mw/
The only difference is that inside the toggle function, I'm trying to call setVisibleSection in the parent, but no dice.
Here is a photo of the console showing the props when the component initially renders, and then after recursion:
I don't think I really understand your 2nd issue. Could you post a fiddle showing the problem?
I think your first issue is that you need to pass the props down to the children. I tried to transcribe your example to your fiddle. You can see by clicking the nodes, the title switched to the node's name.
https://jsfiddle.net/hbjjq3zj/
/**
* Using React 15.3.0
*
* - 2016-08-12: Update to React 15.3.0, class syntax
* - 2016-02-16: Update to React 0.14.7, ReactDOM, Babel
* - 2015-04-28: Update to React 0.13.6
*/
class TreeNode extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: true,
};
}
toggle = () => {
this.setState({visible: !this.state.visible});
this.props.setVisibleSection(this.props.node.title)
};
render() {
var childNodes;
var classObj;
if (this.props.node.childNodes != null) {
childNodes = this.props.node.childNodes.map((node, index) => {
return <li key={index}><TreeNode {...this.props} node={node} /></li>
});
classObj = {
togglable: true,
"togglable-down": this.state.visible,
"togglable-up": !this.state.visible
};
}
var style;
if (!this.state.visible) {
style = {display: "none"};
}
return (
<div>
<h5 onClick={this.toggle} className={classNames(classObj)}>
{this.props.node.title}
</h5>
<ul style={style}>
{childNodes}
</ul>
</div>
);
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: true,
};
}
toggle = () => {
this.setState({visible: !this.state.visible});
};
setVisibleSection(nodeTitle) {
this.setState({
title: nodeTitle
})
}
render() {
return (
<div>
Title: {this.state.title}
<TreeNode
node={tree}
setVisibleSection={this.setVisibleSection.bind(this)}
/>
</div>
);
}
}
var tree = {
title: "howdy",
childNodes: [
{title: "bobby"},
{title: "suzie", childNodes: [
{title: "puppy", childNodes: [
{title: "dog house"}
]},
{title: "cherry tree"}
]}
]
};
ReactDOM.render(
<ParentComponent />,
document.getElementById("tree")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
I am following Chang Wang's tutorial for making reusable React transitions with HOCs and ReactTransitionGroup(Part 1 Part 2) in conjunction with Huan Ji's tutorial on page transitions (Link).
The problem I am facing is that React.cloneElementdoes not seem to be passing updated props into one of its children, while other children do properly receive updated props.
First, some code:
TransitionContainer.js
TransitionContainer is a container component that is akin to App in Huan Ji's tutorial. It injects a slice of the state to it's children.
The children of the TransitionGroup are all an instance of an HOC called Transition (code further down)
import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
class TransitionContainer extends React.Component{
render(){
console.log(this.props.transitionState);
console.log("transitionContainer");
return(
<div>
<TransitionGroup>
{
React.Children.map(this.props.children,
(child) => React.cloneElement(child, //These children are all instances of the Transition HOC
{ key: child.props.route.path + "//" + child.type.displayName,
dispatch: this.props.dispatch,
transitionState: this.props.transitionState
}
)
)
}
</TransitionGroup>
</div>
)
}
}
export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)
Transition.js
Transition is akin to Chang Wang's HOC. It takes some options, defines the componentWillEnter + componentWillLeave hooks, and wraps a component. TransitionContainer (above) injects props.transitionState into this HOC. However, sometimes the props do not update even if state changes (see The Problem below)
import React from 'react';
import getDisplayName from 'react-display-name';
import merge from 'lodash/merge'
import classnames from 'classnames'
import * as actions from './actions/transitions'
export function transition(WrappedComponent, options) {
return class Transition extends React.Component {
static displayName = `Transition(${getDisplayName(WrappedComponent)})`;
constructor(props) {
super(props);
this.state = {
willLeave:false,
willEnter:false,
key: options.key
};
}
componentWillMount(){
this.props.dispatch(actions.registerComponent(this.state.key))
}
componentWillUnmount(){
this.props.dispatch(actions.destroyComponent(this.state.key))
}
resetState(){
this.setState(merge(this.state,{
willLeave: false,
willEnter: false
}));
}
doTransition(callback,optionSlice,willLeave,willEnter){
let {transitionState,dispatch} = this.props;
if(optionSlice.transitionBegin){
optionSlice.transitionBegin(transitionState,dispatch)
}
if(willLeave){
dispatch(actions.willLeave(this.state.key))
}
else if(willEnter){
dispatch(actions.willEnter(this.state.key))
}
this.setState(merge(this.state,{
willLeave: willLeave,
willEnter: willEnter
}));
setTimeout(()=>{
if(optionSlice.transitionComplete){
optionSlice.transitionEnd(transitionState,dispatch);
}
dispatch(actions.transitionComplete(this.state.key))
this.resetState();
callback();
},optionSlice.duration);
}
componentWillLeave(callback){
this.doTransition(callback,options.willLeave,true,false)
}
componentWillEnter(callback){
this.doTransition(callback,options.willEnter,false,true)
}
render() {
console.log(this.props.transitionState);
console.log(this.state.key);
var willEnterClasses = options.willEnter.classNames
var willLeaveClasses = options.willLeave.classNames
var classes = classnames(
{[willEnterClasses] : this.state.willEnter},
{[willLeaveClasses] : this.state.willLeave},
)
return <WrappedComponent animationClasses={classes} {...this.props}/>
}
}
}
options
Options have the following structure:
{
willEnter:{
classNames : "a b c",
duration: 1000,
transitionBegin: (state,dispatch) => {//some custom logic.},
transitionEnd: (state,dispatch) => {//some custom logic.}
// I currently am not passing anything here, but I hope to make this a library
// and am adding the feature to cover any use case that may require it.
},
willLeave:{
classNames : "a b c",
duration: 1000,
transitionBegin: (state,dispatch) => {//some custom logic.},
transitionEnd: (state,dispatch) => {//some custom logic.}
}
}
Transition Lifecycle (onEnter or onLeave)
When the component is mounted, actions.registerComponent is dispatched
componentWillMount
When the component's componentWillLeave or componentWillEnter hook is called, the corresponding slice of the options is sent to doTransition
In doTransition:
The user supplied transitionBegin function is called (optionSlice.transitionBegin)
The default action.willLeave or action.willEnter is dispatched
A timeout is set for the duration of the animation (optionSlice.duration). When the timeout is complete:
The user supplied transitionEnd function is called (optionSlice.transitionEnd)
The default actions.transitionComplete is dispatched
Essentially, optionSlice just allows the user to pass in some options. optionSlice.transitionBegin and optionSlice.transitionEnd are just optional functions that are executed while the animation is going, if that suits a use case. I'm not passing anything in currently for my components, but I hope to make this a library soon, so I'm just covering my bases.
Why Am I tracking transition states anyway?
Depending on the element that is entering, the exiting animation changes, and vice versa.
For example, in the image above, when the blue enters, red moves right, and when the blue exits, red moves left. However when the green enters, red moves left and when the green exits, red moves right. To control this is why I need to know the state of current transitions.
The Problem:
The TransitionGroup contains two elements, one entering, one exiting (controlled by react-router). It passes a prop called transitionState to its children. The Transition HOC (children of TransitionGroup) dispatches certain redux actions through the course of an animation. The Transition component that is entering receives the props change as expected, but the component that is exiting is frozen. It's props do not change.
It is always the one that is exiting that does not receive updated props. I have tried switching the wrapped components (exiting and entering), and the issues is not due to the wrapped components.
Images
On-Screen Transition:
Transition in React DOM
The exiting component Transition(Connect(Home))), in this case, is not receiving updated props.
Any ideas why this is the case? Thanks in advance for all the help.
Update 1:
import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
var childFactoryMaker = (transitionState,dispatch) => (child) => {
console.log(child)
return React.cloneElement(child, {
key: (child.props.route.path + "//" + child.type.displayName),
transitionState: transitionState,
dispatch: dispatch
})
}
class TransitionContainer extends React.Component{
render(){
let{
transitionState,
dispatch,
children
} = this.props
return(
<div>
<TransitionGroup childFactory={childFactoryMaker(transitionState,dispatch)}>
{
children
}
</TransitionGroup>
</div>
)
}
}
export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)
I've updated my TransitionContainer to the above. Now, the componentWillEnter and componentWillLeave hooks are not being called. I logged the React.cloneElement(child, {...}) in the childFactory function, and the hooks (as well as my defined functions like doTransition) are present in the prototype attribute. Only constructor, componentWillMount and componentWillUnmount are called. I suspect this is because the key prop is not being injected through React.cloneElement. transitionState and dispatch are being injected though.
Update 2:
import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
var childFactoryMaker = (transitionState,dispatch) => (child) => {
console.log(React.cloneElement(child, {
transitionState: transitionState,
dispatch: dispatch
}));
return React.cloneElement(child, {
key: (child.props.route.path + "//" + child.type.displayName),
transitionState: transitionState,
dispatch: dispatch
})
}
class TransitionContainer extends React.Component{
render(){
let{
transitionState,
dispatch,
children
} = this.props
return(
<div>
<TransitionGroup childFactory={childFactoryMaker(transitionState,dispatch)}>
{
React.Children.map(this.props.children,
(child) => React.cloneElement(child, //These children are all instances of the Transition HOC
{ key: child.props.route.path + "//" + child.type.displayName}
)
)
}
</TransitionGroup>
</div>
)
}
}
export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)
After further inspection of the TransitionGroup source, I realized that I put the key in the wrong place. All is well now. Thanks so much for the help!!
Determining Entering and Leaving Children
Imagine rendering the sample JSX below:
<TransitionGroup>
<div key="one">Foo</div>
<div key="two">Bar</div>
</TransitionGroup>
The <TransitionGroup>'s children prop would be made up of the elements:
[
{ type: 'div', props: { key: 'one', children: 'Foo' }},
{ type: 'div', props: { key: 'two', children: 'Bar' }}
]
The above elements will be stored as state.children. Then, we update the <TransitionGroup> to:
<TransitionGroup>
<div key="two">Bar</div>
<div key="three">Baz</div>
</TransitionGroup>
When componentWillReceiveProps is called, its nextProps.children will be:
[
{ type: 'div', props: { key: 'two', children: 'Bar' }},
{ type: 'div', props: { key: 'three', children: 'Baz' }}
]
Comparing state.children and nextProps.children, we can determine that:
1.
{ type: 'div', props: { key: 'one', children: 'Foo' }} is leaving
2.
{ type: 'div', props: { key: 'three', children: 'Baz' }} is entering.
In a regular React application, this means that <div>Foo</div> would no longer be rendered, but that is not the case for the children of a <TransitionGroup>.
How <TransitionGroup> Works
So how exactly is <TransitionGroup> able to continue rendering components that no longer exist in props.children?
What <TransitionGroup> does is that it maintains a children array in its state. Whenever the <TransitionGroup> receives new props, this array is updated by merging the current state.children and the nextProps.children. (The initial array is created in the constructor using the initial children prop).
Now, when the <TransitionGroup> renders, it renders every child in the state.children array. After it has rendered, it calls performEnter and performLeave on any entering or leaving children. This in turn will perform the transitioning methods of the components.
After a leaving component's componentWillLeave method (if it has one) has finished executing, it will remove itself from the state.children array so that it no longer renders (assuming it didn't re-enter while it was leaving).
Passing Props to Leaving Children?
Now the question is, why aren't updated props being passed to the leaving element? Well, how would it receive props? Props are passed from a parent component to a child component. If you look at the example JSX above, you can see that the leaving element is in a detached state. It has no parent and it is only rendered because the <TransitionGroup> is storing it in its state.
When you are attempting to inject the state to the children of your <TransitionGroup> through React.cloneElement, the leaving component is not one of those children.
The Good News
You can pass a childFactory prop to your <TransitionGroup>. The default childFactory just returns the child, but you can take a look at the <CSSTransitionGroup> for a more advanced child factory.
You can inject the correct props into the children (even the leaving ones) through this child wrapper.
function childFactory(child) {
return React.cloneElement(child, {
transitionState,
dispatch
})
}
Usage:
var ConnectedTransitionGroup = connect(
store => ({
transitionState: state.transitions
}),
dispatch => ({ dispatch })
)(TransitionGroup)
render() {
return (
<ConnectedTransitionGroup childFactory={childFactory}>
{children}
</ConnectedTransitionGroup>
)
}
React Transition Group was somewhat recently split out of the main React repo and you can view its source code here. It is pretty straightforward to read through.