简体   繁体   中英

setState while looping through an array of props - React ComponentDidUpdate

I am working on a project and needed to setState after componentDidMount.(The props am expecting in the child component are derived at mount. Hence i can only setState after) Only option i was able to come up with was, using componentDidUpdate.

The props parent component is an array derived from an axios fetched data.

The goal here is to loop though the array and fetch data for each from the URL showing in the code below to then setState of the child component.

Trying what i normally do, I could not stop the infinite loop fired at componentDidUpdate.

Here is my code.

Parent

  render(){

  return (
    <div className="App">

      <EachCountry countryList= {this.state.CountriesList}/>

    </div>

Child component

     async componentDidUpdate(prevProps, prevState, snapshot){
    if(this.state.countryList.length < this.props.countryList.length){
        await this.props.countryList.map(m=>{
                axios ({
                    method:"get",
                    url:`/countryupdate/${m}`
                }).then(res=>{
                    console.log(res.data)
                    this.setState(crntState=>({
                        countryList:[...crntState.countryList, res.data]
                    }))  
                })
        })
        }    
    }

console log works perfectly fine. But when i tried to setState, i run into infinite loop with something like 5000 plus error messages.

And my other trick was

 async componentDidUpdate(prevProps, prevState, snapshot){
    if(this.state.countryList.length < this.props.countryList.length){
        await this.props.countryList.map(m=>{
                var newdata =  axios ({
                    method:"get",
                    url:`/countryupdate/${m}`
                })
                console.log(newdata)
                    this.setState(crntState=>({
                        countryList:[...crntState.countryList, newdata.data]
                    }))  
        })
        }    
    }

And this one returns promises and not the needed data.

Help Fam

What am i missing?

Your issue is likely caused by derived state: state that is dependent on props and is an anti pattern in react: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state

See below on a plausible workaround, though its recommended you restructure your data flow.

Try something like this to first only send 1 update to state:

async componentDidMount(){

  //variable to store new data
  const allNewData = [];
  
  //an async data fetcher
  const getNewData = async(m) => {
    let newData = await axios({
      method: "get",
      url: `/countryupdate/${m}`
    })
    allNewData.push(newData.data);
  }
  
  //an async for loop
  async function updateData() {
    for (const m of countryList) {
      await getNewData(m);
    }
    this.setState(crntState => ({
      countryList: [...crntState.countryList, ...allNewData]
    }))
  }

  await updateData();
}

If the above doesn't work (which it might not), then use getDerivedStateFromProps instead of componentDidMount and replace setState with a return obj

static getDerivedStateFromProps(props, state) {
  if (this.state.countryList.length < this.props.countryList.length) {
      ...
      return {
        countryList: [...state.countryList, ...allNewData]
      };
     }

     let newState = await updateData();
     return newState; 
}

If that doesn't work, then revert back to componentDidMount and use shouldComponentUpdate for the conditional

shouldComponentUpdate(nextProps, nextState) {
    return this.state.countryList.length != nextState.countryList.length; 
}

Incase I didn't get the syntax just right look at this code snippet

 function mockAxios(m) { return new Promise(function(resolve, reject) { setTimeout(() => resolve({ data: `${m}'s data` }), 1000) }); } function setState(arr) { console.log(arr); console.log("state has been set") } const countryList = ["A", "B", "C", "D", "E"]; /////////////////////////////////////////////// async function componentDidMount() { const allNewData = []; async function getNewData(m) { let newData = await mockAxios(m); allNewData.push(newData.data); } async function updateData() { for (const m of countryList) { await getNewData(m); console.log("fetching data...") } setState(allNewData); } await updateData(); } componentDidMount();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM