58

I want to change the value for the hasSubmit key, like in the First Code section. I know this is not recommended. But the second code is asynchronous and I don't want to use the callback function of setState.

  • What is the difference of this.state and setState?
  • Is there any way to change state value hasSubmit immediately?

First Code:

this.state.hasSubmit = false
this.setState({})
//Code that will use `hasSubmit`.

Second code:

this.setState({
   hasSubmit: false,
});
//Code that will use `hasSubmit`.

ADD:

The scenario is that:

  1. hasSubmit set false in getInitialState().
  2. hasSubmit will change to false when I click submit button.
  3. hasSubmit will change to true when submitted.

First click submit has no problem and hasSubmit will be set to true.

But second click submit will be wrong using the Second asynchronous code, because the hasSubmit is still true, while the First Code can resolve the problem.

Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
DanielJyc
  • 743
  • 1
  • 5
  • 12
  • what is your endgoal for wanting this? could you elaborate. i can refer you to the docs, but i assume you already did that, so i would like to know what u are trying to achieve. – Danillo Felixdaal Mar 08 '16 at 15:08

5 Answers5

57

Here's what the React docs say:

NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

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. setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate().

If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.

It's always sensible to use APIs in the way they were designed. If the docs say don't mutate your state, then you'd better not mutate your state.

Whilst setState() might be technically asynchronous, it's certainly not slow in any noticeable way. The component's render() function will be called in pretty short order.

One drawback of setting state directly is that React's lifecycle methods - shouldComponentUpdate(), componentWillUpdate(), componentDidUpdate() - depend on state transitions being called with setState(). If you change the state directly and call setState() with an empty object, you can no longer implement those methods.

Another is that it's just bad programming style. You're doing in two statements what you could be doing in one.

Moreover, there's no actual benefit here. In both cases, render() is not going to be triggered until after setState() (or forceUpdate()) is called.

You claim a need to do this without actually explaining what that need is. Perhaps you'd like to detail your problem a little more. There's probably a better solution.

It's best to work with the framework rather than against it.

UPDATE

From the comments below:

The need is that I want use the changed hasSubmit in below.

OK, I understand now. If you need to immediately use the future state property, your best bet is just to store it in a local variable.

const hasSubmit = false;

this.setState({
  hasSubmit: hasSubmit
});

if (hasSubmit) { 
  // Code that will use `hasSubmit` ...
David L. Walsh
  • 24,097
  • 10
  • 61
  • 46
  • The need is that I want use the changed `hasSubmit ` in below. – DanielJyc Mar 09 '16 at 02:30
  • I tried the first code. The method `componentWillUpdate()`, `componentDidUpdate()`, `render()` also will be called and the value of `hasSubmit` is changed to `true`. – DanielJyc Mar 09 '16 at 02:38
  • 1) I see. I've updated my answer to address this. 2) Those lifecycle methods can no longer meaningfully compare `this.state` with `nextState` or `prevState`. – David L. Walsh Mar 09 '16 at 03:21
  • The fact that you say to maintain a local variable is not good imo. This could be achieved by a callback for the setstate method as the documentation. – Haswin Jan 31 '18 at 05:06
  • Adding an asynchronous callback function is far more unwieldy than a simple local variable. It also misses the point. Querying the state is unnecessary. You do not need to *get* that which you have just *set*! – David L. Walsh Jan 31 '18 at 06:07
8

If you want to change state and trigger a re-render by react: Use the second code.

  this.setState({
    hasSubmit: false,
  });

Problems/ errors with first code:

  this.state.hasSubmit = false      // Updates state directly: 
                                    // You are not supposed to do this
                                    // except in ES6 constructors
  this.setState({})                 // passes an empty state to react.
                                    // Triggers re-render without mutating state
wintvelt
  • 13,855
  • 3
  • 38
  • 43
  • Clear answer and understandable – Prem Jun 11 '18 at 03:26
  • You say `this code does not use setState callback:` but in both examples you use `setState` -- is this a typo? – Jonathan Jan 30 '19 at 00:58
  • `setState` can take an optional callback parameter. Which can be used to do something after the state is updated. It was not really a typo: I used `setState`, but without passing any callback parameter. It was admittedly confusing, so I removed the reference ;) – wintvelt Feb 18 '19 at 07:06
5

this.setState maintains the react component's life cycle and doesn't seem like mutating variables (even though internally it does mutate state). So the one way flow in react cycle is maintained without any side effects.

The caveat is with using this.setState doesn't work with constructors in ES6 classes. We need to use this.state = pattern rather than this.setState in ES6 constructors

Aditya Singh
  • 15,810
  • 15
  • 45
  • 67
  • 2
    Yep. Very nice observation that `this.setState` syntax isn't allowed in ES6 class constructor. Possibly because in the constructor you aren't modifying the state but initializing it instead for the first time. – RBT Nov 03 '17 at 06:41
  • 1
    this.setState isn't allowed in constructors since setState can be batched and is asynchronous, so it could mean that the component can end up having a different state that what was expected of it or was initialised with. AJAX calls are discouraged too for the same reason. – supi Jul 13 '18 at 22:02
5

You should never ignore the documentation advice. At the time of writing, setState allow second argument which is a callback function when the setState and re-render had finished. Since you never provides us how your code gonna use hasSubmit value, I believe some other may find this useful when they want to make sure the hasSubmit had been changed.

Sany Liew
  • 1,615
  • 21
  • 25
  • I believe this should be the answer. There is no need to maintain another local variable to hold the value as the top answer suggests. – Haswin Jan 31 '18 at 05:05
2

You should use this.forceUpdate() in first example to force update the state. For example:

this.state.hasSubmit = false;
this.forceUpdate();

But it is better to use this.setState because it is init native check-state mecanizm of React engine which is better then force update.

If you just update any param of this.state directly without setState react render mecanizm will not know that some params of state is updated.

Dmitriy
  • 3,745
  • 16
  • 24
  • 3
    As in the official docs [here](https://facebook.github.io/react/docs/component-api.html#forceupdate): "Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render()." In this particular case, forceUpdate() is not a good solution. – wintvelt Mar 08 '16 at 15:03
  • For really deeply nested state JSON objects, such as a deeply nested array, updating the state with `setState` is a pain – Jonathan Jan 30 '19 at 01:00