6

I have a parent component which has 1 child. I am updating my child by passing data through props. initially, it works fine but when I click on a button and update the state using setState the child gets rendered with old values by the time setState is finished. I have solved it using componentWillReceiveProps in the child but is this the right way?

In the below code if I do setState in filterResults function it won't update the Emplist component .

import React, { Component } from 'react';
import {Search} from './search-bar'
import Emplist from './emplist'

class App extends Component {
    constructor(props){
        super(props);
        this.emp=[{
            name:'pawan',
            age:12
        },
        {
            name:'manish',
            age : 11
        }]
        this.state={emp:this.emp};
        this.filterResults=this.filterResults.bind(this);
    }

    filterResults(val)
    {
        if(this.state)
        {
            let filt=[];
            filt.push(
                this.emp.find(e=>{
                    return e.age==val
                })
            );
            this.setState({emp:filt});
        }
    }

    render() {
        return (
            <div className="App">
                <Search filterResults={this.filterResults}/>
                <Emplist emp={this.state.emp}/>
            </div>
        );
    }
}
export default App;

EmpList Componet

import React,{Component} from 'react'

export default class Emp extends Component
{
    constructor(props){
        super(props);
        
        this.emplist=this.props.emp.map(e=>{return <li>{e.name}</li>});
        this.next=this.emplist;
    }

    componentWillReceiveProps(nextProps,nextState,prevProps,prevState,nextContext,prevContext){
        // this.props.updated(this.props.empo);
        this.next=nextProps.emp[0];
        if(this.next)
            this.emplist= nextProps.emp.map(e=>{return <li>{e.name}</li>});
    }

    render(){
        if(!this.next)
            return <div>name not found</div>
        else
            return (
                <div>
                    <br/>
                    <p>The list is here</p>
                    <ul>
                        {this.emplist}
                    </ul>
                </div>
            )
    }
}
pKay
  • 1,832
  • 4
  • 16
  • 23
  • Yes that's the correct way. if you do not need to use those values for some complex things or anything else in child, you can directly use them with `this.props.someValue` will work as same. – Brijesh Bhakta Nov 25 '17 at 08:14
  • in child, i used nextProps since i was not able to get updated props when the child renders . how this.props.someValue will work in child ? – pKay Nov 25 '17 at 08:17
  • 1
    Can you give full code of your child component? That will be much easy. Please update your question with the code. – Brijesh Bhakta Nov 25 '17 at 08:19
  • Using Redux will be much helpful and stress free. Traditional params passing method will be hectic sometimes when you have multiple routes. – Edison D'souza Nov 25 '17 at 08:45
  • @pKay gave an answer. Please check. – Brijesh Bhakta Nov 25 '17 at 09:02
  • Possible duplicate of [Pass props to parent component in React.js](https://stackoverflow.com/questions/22639534/pass-props-to-parent-component-in-react-js) – Mario Nikolaus Nov 25 '17 at 10:21
  • @BrijeshBhakta i am learning redux , hope that will solve the problem . thanks . – pKay Nov 25 '17 at 13:04
  • @pKay, sure it will solve the problem. But I suggest to play around with just react and then move to redux. – Brijesh Bhakta Nov 26 '17 at 06:03

3 Answers3

8

If you want to pass from parent to child you can pass using props and if you wan t to do reverse than you can pass one function from parent to child and than use this passed function to send something back to parent.

child will look something like this

class Reciepe extends Component{
    render(){
        const { title, img, instructions } = this.props;
        const ingredients=this.props.ingredients.map((ing,index)=>(<li key={index} >{ing}</li>));
        return (
            <div className='recipe-card'>
                <div className='recipe-card-img'> <img src={img} alt={title}/> </div>
                <div className='recipe-card-content'>
                    <h3 className='recipe-title'>Reciepe {title}</h3>
                    <ul> {ingredients} </ul>
                    <h4>Instructions:</h4>
                    <p>{instructions}</p>
                </div>
            </div>
        )
    }
}

parent will look something like this

class RecipeList extends Component{
    render(){
        return (
            <div style={{'display':'flex'}}>
                {this.props.recipes.map((item,index)=>(
                    <Recipe key={index}
                        title={item.title}
                        ingredients={item.ingredients}
                        instructions={item.instructions}
                        img={item.img}
                    />
                ))}
            </div>
        )
    }
}
  • you are passing some properties in Recipe component . this will work first time but when when i update one of the property (i.e title in your case ) by using setState then child compoent will get rendered with old props since setState is async it takes time to update. so how do we solve that. i have posted the code also . you can have a look – pKay Nov 25 '17 at 08:38
0

The problem is that you are assigning the values to this which is not a good practice. Check where to declare variable in React here.

If you are not using the props to do any complex operations. This should work.

EmpList Componet

import React, {Component} from 'react'

export default class Emp extends Component {

    constructor(props) {
        super(props);
    }

    render() {

        if (!this.next)
            return <div>name not found</div>;
        else
          return (
              <div>
                  <br/>
                  <p>The list is here</p>
                  <ul>
                      {this.props.emp && this.props.emp.map(e => <li>{e.name}</li>)}
                  </ul>
              </div>
            )
    }
}
Brijesh Bhakta
  • 1,280
  • 1
  • 8
  • 14
0

Your next and emplist class properties are directly derivable from your props and hence you don't actually need them. You could do it in the following way

import React,{Component} from 'react'

export default class Emp extends Component{

    render(){
        const { emp } = this.props;
        if(!emp || emp.length === 1)
            return <div>name not found</div>
        else {
           return (
              <div> 
                 <br/> <p>The list is here</p>
                 <ul>
                   {emp.map(e=>{return <li>{e.name}</li>});}
                 </ul>
              </div>
           )
       }
    }
}

However in cases when you do what to make really complex decisions based on props, a combination of componentWillReceiveProps and componentDidMount/componentWillMount is the right place to do it.

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400