1

Assuming I have the following code;

ComponentDidMount(){
  const res = fetch(smth) 

  this.setState({data: res}) // first setState

  const res2 = fetch(another)

  this.setState({data2: res2}) // second setState
}

The issue is that both setState will work. I was thinking that first setState calls render() and the component reloads and the code after the first setState will not work

Why?

Afia
  • 683
  • 5
  • 17
Sunstrike527
  • 515
  • 1
  • 4
  • 17
  • 1
    this is componentDidMount function. It calls only once, when page loaded. setState if re-render only code which are inside render() function, not whole page. – demkovych Jan 24 '20 at 10:24
  • https://stackoverflow.com/questions/33613728/what-happens-when-using-this-setstate-multiple-times-in-react-component – Jurica Smircic Jan 24 '20 at 10:25
  • is your question "why does the second setState fire?" (explained by @Reza below) or "how can I ensure that the second setState only fires after the initial page render?" – jymbob Jan 24 '20 at 10:31
  • @jymbob yes , my question was why does the 2nd setStaet fire . – Sunstrike527 Jan 24 '20 at 10:39
  • look at https://css-tricks.com/understanding-react-setstate/ – Sohan Jan 24 '20 at 10:42

3 Answers3

4

According to the docs:

componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)....... You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state.

Which means that all the codes in "componentDidMount()" get called before the user sees the result in render().

Reza
  • 56
  • 5
  • so it means that this.setState will call render() as many times as it will be invoked in didMount (f.e 3 setStates - > 3 render()) and only then the user will see the screen , right ? – Sunstrike527 Jan 24 '20 at 10:31
  • Yes, to my understanding, the render will be called 3 times, but the user only sees the last one. Which is exactly why (according to docs) this method can cause performance issues if you have too many setStates and it's better to assign the initial state in the constructor(), rather than in componentDidMount(). – Reza Jan 24 '20 at 10:35
2

That's not the way set setState works.

If you want to guarantee that something should be called after setState you should try this:

async componentDidMount () {
   const res = await fetch(smth);

   this.setState({data: res}, async () => {
      // put the second fetch here
      const res2 = await fetch(another);

      this.setState({data2: res2});
   });
}
evolon
  • 1,276
  • 1
  • 10
  • 18
2

I will try to exlain why it is async and how you can wrap multiple state together,

1) setState actions are asynchronous and are batched for performance gains. This is explained in the documentation of setState.

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

2) Why would they make setState async as JS is a single threaded language and this setState is not a WebAPI or server call?

This is because setState alters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.

Thus the setState calls are asynchronous as well as batched for better UI experience and performance.

A simple example to demonstrate this, is that if you call setState as a reaction to a user action, then the state will probably be updated immediately (although, again, you can't count on it), so the user won't feel any delay, but if you call setState in reaction to an ajax call response or some other event that isn't triggered by the user, then the state might be updated with a slight delay, since the user won't really feel this delay, and it will improve performance by waiting to batch multiple state updates together and re-render the DOM fewer times.

if you have lots of states to update at once, group them all within the same setState:

  this.setState({foo: "one"}, () => {
     // do something
    this.setState({bar: "two"});
});

So in you above code should be similar to,

   async ComponentDidMount() {
   const res = await fetch(smth);

   this.setState({data: res}, async () => {
      //do something more.... 
      const res2 = await fetch(another);

      //setState again .....
      this.setState({data2: res2});
   });
}
Sohan
  • 6,252
  • 5
  • 35
  • 56