简体   繁体   中英

Strange double execution of setState code in react

I have a component, which displays some todos list like

const todosData = [
  {
      id: 1,
      text: "Take out the trash",
      completed: true
  },
  {
      id: 2,
      text: "Grocery shopping",
      completed: false
  },
];

And I have a button that triggers one simple change: it adds char "1" to the text property of the first element. And my handler looks like this

    handleChange() {
      this.setState(prevState => {
          const updatedTodos = prevState.todos.map(todo => {
              if (todo.id === 1) {
                todo.text += 1
                console.log(todo.text)
                // here i get Take out the trash1, Take...trash111, Take...trash11111 and etc
              }  
              return todo
          })
          return {
              todos: updatedTodos
          }
      })
  }

and simple rendering

render() { 
        return (
            <ul className="todo-list">
              {this.state.todos.map(item => <li>{item.text}</li>)}
              <button onClick={this.handleChange}>change me</button>  
            </ul>
        )    
    }

so i expect after clicking to see "Take out the trash1" in console and the same text on the page. Instead of this i see "Take out the trash1" in console as expected, but on the page i see "Take out the trash11", and then "Take out the trash1111" as this code works double, but then why in console i see only result of first work: "Take out the trash1", "Take out the trash111"?

Yes, i understand that it is not right way to change item like this and i should do

todo = {...todo, text: todo.text += 1} 

and if i write this way - everything works correct, and there are other lacks in this code and actually this code is not mine, but i just wonder how this magic happens:

  1. why it works twice
  2. why i see only first output in console

The sandbox example https://codesandbox.io/s/objective-margulis-0phvx?file=/src/App.js

This is an interesting question. You can try with a letter (or any string) and the same thing will happen. You can also append a string to the start and the same thing will happen.

eg

todo.text = 'boo' + todo.text + 'doo'

My completely uneducated guess was that somewhere in the internals of React it does some kind of string comparison and works out what the difference is; and that maybe your code modifies the string in the original object, and that by the time it comes to apply it, it applies it to the already modified string.

But... if I do:

const t = Date.now();
while(Date.now() < t + 1);
todo.text = 'a_' + todo.text + '_b_' + t;

The t portion of the string does not stay the same.

Interestingly, if you do:

todo.text = 'a_' + todo.text + alert(todo.text);

It does alert twice (even though, as you've mentioned, it only console.logs once).

So it looks like this code does run twice for some reason. I've no idea why there's only one console.log message.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM