4

I have a function that sets state twice, however - the second setState has to occur after 500ms since first setState has occured (animation purposes).

Code looks like:

const showAnimation = () => {
   this.setState({ hidden: false });

   setTimeout(() => {
      this.setState({ hidden: true });
   }, 500);
};

However - if I do it this way, React somehow merges these two setState's into one and my animation doesn't work as expected.

But, if I use a hack:

const showAnimation = () => {
   setTimeout(() => {
      this.setState({ hidden: false });
   }, 0);  // ------------------------------> timeout 0

   setTimeout(() => {
      this.setState({ hidden: true });
   }, 500);
};

It works as expected. But still, I don't really like it and Im afraid that it may be some kind of a hack. Is there any better solution for such case? Thanks :)

Patrickkx
  • 1,740
  • 7
  • 31
  • 60

4 Answers4

4

As setState are async in React you might not get updated state immediately but setState gives you prevState param in setState function to get last updated state so you won't merge state

The syntax goes like this in your case

this.setState((prevState) => { hidden: false }, () => {
      setTimeout(() => {
        this.setState({ hidden: !prevState.hidden });
      }, 500);
    });

just update your value to the updated state using prevState

If I understand your problem correct this should work fine
Please let me know if more clarification required

keikai
  • 14,085
  • 9
  • 49
  • 68
Sachin Kumar
  • 125
  • 5
1

If you try something like that:

const showAnimation = () => {
    this.setState({ hidden: false }, () => {
      setTimeout(() => {
        this.setState({ hidden: true });
      }, 500);
    }
  }
Nicolas Takashi
  • 1,550
  • 2
  • 10
  • 11
1

I would personally use animations within JS if you are looking to time it without setTimeout. However this may be down to the face that 'setState' is async within react.

similar : Why is setState in reactjs Async instead of Sync?

However react does expose a callback within setState - this works for me

this.setState(
            { hidden : false },
            () => {
                setTimeout(()=>{this.setState({hidden : true})}, 500)
            }
        );
James Bass Davies
  • 408
  • 1
  • 3
  • 11
  • i would def use css or js for the animations, but if not, callback is cleanest way – Eric Hasselbring Jun 06 '18 at 16:19
  • it causes warning in the console `Warning: Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.` – Patrickkx Jun 06 '18 at 16:30
  • Usually promises are a good work around for async operations :D – Eddie D Jun 06 '18 at 16:33
  • 1
    the setState callback handles async just as well as a promise but with less additional code. The update during an existing state transition is an issue with calling this function in the render function – Eric Hasselbring Jun 06 '18 at 16:38
0

For rendering performance, react batches calls to setState such that sequential calls will be executed together and will often be reflected in the same render cycle. Per the docs:

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.

In order to ensure that the first setState has been executed prior to your second call, you can pass setState a callback as the second argument. It's not perfect, but something like the following will ensure that your second call to setState will only happen once hidden: false.

const showAnimation = () => {
this.setState({ hidden: false }, () => setTimeout(() => {
   this.setState({ hidden: true });
  }, 500););
};
john_mc
  • 1,333
  • 11
  • 16