0

I've built a React app to search recordings, that has the following structure:

SearchScreen => SearchParms
             => SearchResults

SearchScreen is rendered like this:

return(
<Fragment> 
 <div className='containerCR'><SearchParms getRecordingsHandle = {this.handleGetRecordings} /> 
 <SearchResults recordings = {this.state.recordings}/>
</Fragment>)

A button on the Search Parms screen calls this.handleGetRecordings, which calls an async function to get records, adds them to state.recordings and then passes them to the SearchResults screen.

Problem is, because it's async it passes the recordings object before it's populated.

handleGetRecordings sets the state for the search parameter ani. It needs to be done in a then because it doesn't set it immediately:

handleGetRecordings = (ani) =>
  {
    console.log(this.state.ani);
    console.log("Passed value: ", ani);
    this.setState({
      ani:ani
    }, () => {

      console.log("Set state finished:", this.state.ani);

      this.fetchRecordings();
    });

  }  

The fetchRecordings function gets the data and then calls setRecordings:

fetchRecordings = async () => {
    console.log("getting recordings for: ", this.state.ani);
      const myInit = {
          headers: {}, 
          response: true, 
          queryStringParameters: {
            ani: encodeURIComponent(this.state.ani)
          }
        };

        console.log("Params", myInit);

        API.get(myAPI, path, myInit)
        .then(response => {
          console.log("Response:", response)
          let newRecordings = this.state.recordings;
          newRecordings.push(response)
          this.setRecordings(newRecordings[0].data)
  
        })
        .catch(error => {
          console.log(error)
        })
      
  }

setRecordings writes the list of recordings to the state, ready to pass to the SearchResults page, except by the time it gets here its missed its chance!

setRecordings =(recordings) =>
  {
    console.log("Passed value: ", recordings);
    this.setState({
      recordings:recordings
    }, () => {      
      console.log("Current State: ", this.state.recordings);
      
    });
  } 

What's the correct way of doing this?

Nick Wright
  • 87
  • 1
  • 9

1 Answers1

1

Reading the state the way you're doing here, is not guaranteed to give you the latest state possible.

let newRecordings = this.state.recordings

In order to use your old state to create a new one, you need to pass an updater function to setState as the first argument. Look at the examples here in React docs: https://reactjs.org/docs/react-component.html#setstate
Then you can create your new state based on your old state and set it as the new state by returning it from your updater function. This way you can be sure that your new state is what you expect it to be.

Moreover, since your state is an array, its always passed/refered to by reference and React always suggests to not mutate a state (here, we're mutating it by pushing a value to it), but instead, create a new one by cloning the previous one. So the final setState should look like this:

this.setState((prevState) => {
  return {
    recordings: [...prevState.recordings, response],
  }
})
Kavian Rabbani
  • 934
  • 5
  • 15