mottosson
mottosson

Reputation: 3753

Why do I need setTimeout to update state in React component

Why do I need to wrap my setValue with a setTimeout in my React component?

This works

<Button onClick={() => acceptOne()}>
  set value with setTimeout
</Button>

const acceptOne = () => {
  setTimeout(() => {
    setEditMode(false);
  });
};

This does not...

<Button onClick={() => acceptTwo()}>
  set value without setTimeout
</Button>

const acceptTwo = () => {
  setEditMode(false);
};

Here is a CodeSandbox example for full code to demonstrate the issue.

I'm new to React and already had to use setTimout to get state to change like this. I feel this is most likely a beginner thing and there are some React specific things I don't yet understand. What is going on here, why doesn't setValue work when I don't use setTimeout?

Upvotes: 0

Views: 109

Answers (4)

Jai
Jai

Reputation: 74738

It is happening because of the event propagation. As you can see you have bound an event to the parent div. So, whenever you click on button, parent div's event also executes.

Why it works with setTimeout?

Because it executes after div's event because it is been pushed to other async process and it takes time to execute.

You can add event.stopPropagation() to stop the event to bubble up in the DOM:

  const acceptOne = e => {
    e.stopPropagation();
    setEditMode(!editMode);
  };

  const acceptTwo = e => {
    e.stopPropagation();
    setEditMode(!editMode);
  };

Now change the event either to:

onClick={acceptOne}
onClick={acceptTwo}

or:

onClick={e => acceptOne(e)}
onClick={e => acceptTwo(e)}  

Updated codesandbox

Upvotes: 2

Nver Abgaryan
Nver Abgaryan

Reputation: 621

https://codesandbox.io/s/charming-ganguly-mg4zx I fixed it, there is the link. So this is because of after the second click on the input it runners parent div click too because this input wrapped into a div element, <div onClick={() => setEditMode(!editMode)}> So when you put there setTimeout the second input click runs before the parent div element click function, that's why it works. This is specifically connected with the javascript event loop, watch the video about event loop and you will understand whats happens there https://www.youtube.com/watch?v=8aGhZQkoFbQ&t=54s

Upvotes: 0

Squiggs.
Squiggs.

Reputation: 4474

  <div onClick={() => setEditMode(true)}>

this is your problem. The parent DIV that wraps your buttons event is getting called as well as your button click. The delay that the setTimeout provides allows it to come in after the parent div, and therefore works. You can either restructure your HTML or do some event stopPropogation stuff to handle it.

Upvotes: 0

Yash
Yash

Reputation: 919

Issue was with the top line in your code <div onClick={() => setEditMode(true)}>

Review the updated snippet - https://codesandbox.io/s/charming-http-b637o

Logic is very well explained by Jai in his answer.

Upvotes: 0

Related Questions