Eris Suryaputra
Eris Suryaputra

Reputation: 23

React if statement in asynchronous loop question

Help, I cannot seem to figure out how to solve this problem:

array.map(object => {
   console.log(this.state.newArr) // [] didn't update despite setState
   if (this.state.newArr.length === 0) {
      axios.get('http://localhost/')
      .then(res => {
         this.setState(prevState => ({ newArr: [...prevState.newArr, res] })) // setState here
      })
   }
})

Edit: updated code implemented the answers, but still doesn't work atm.

Upvotes: 0

Views: 68

Answers (2)

Quentin Grisel
Quentin Grisel

Reputation: 4987

Async/await pattern can be a solution. Never tried it in a map but it would be something like this :

array.map(async object => {
  if (this.state.newArr.length === 0) {
     const response = await axios.get('http://localhost/');
     await this.setStateAsync({ newArr: [...this.state.newArr, response] });
     // or without the setStateAsync 
     // this.setState(prevState => ({ newArr: [...prevState.newArr, response] })); 
  }
});

const setStateAsync = state => {
  return new Promise((resolve) => {
    this.setState(state, resolve)
  });
}

Upvotes: 1

Brian Thompson
Brian Thompson

Reputation: 14365

setState is async, meaning when setting state in a loop you will not yet have the updated state values. When you make state updates based on previous state values, you should use the updater form of setState.

Example of whats going wrong

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: []
    }
  }
  
  componentDidMount() {
    [1,2,3].forEach((i) => {
      // setState is async, 
      // so it has not updated by the next loop
      // meaning its the same every cycle
      console.log(this.state.arr)
      this.setState({ arr: [...this.state.arr, i] });
    })
  }
  
  render() {
    // Only the last enty is in the array
    console.log('final', this.state.arr)
    return (<div>test</div>);
  }
}

ReactDOM.render(<Example/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Example of the correct way

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: []
    }
  }
  
  componentDidMount() {
    [1,2,3].forEach((i) => {
      // Use the updater form so you are guaranteed
      // to have the most recent copy of state each update
      this.setState((prevState) => ({ arr: [...prevState.arr, i] }));
    })
  }
  
  render() {
   console.log('final: ', this.state.arr)
    return (<div>test</div>);
  }
}

ReactDOM.render(<Example/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Another feature of setState that may come in handy here is the callback option. You may pass a second argument to setState that is a function. That function will be called after the state is updated, so you can add logic there that is dependent on those updated values.

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: []
    }
  }
  
  componentDidMount() {
    [1,2,3].forEach((i) => {
      this.setState((prevState) => ({ arr: [...prevState.arr, i] }), () => {
        console.log('updated: ', this.state);
      });
      console.log('not updated: ', this.state);
    })
  }
  
  render() {
    return (<div>test</div>);
  }
}

ReactDOM.render(<Example/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 0

Related Questions