【问题标题】:Strange double execution of setState code in react反应中setState代码的奇怪双重执行
【发布时间】:2020-10-27 08:18:37
【问题描述】:

我有一个组件,它显示一些待办事项列表,例如

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

我有一个按钮可以触发一个简单的更改:它将字符“1”添加到第一个元素的文本属性中。我的处理程序看起来像这样

    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
          }
      })
  }

简单的渲染

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

所以我希望在单击后在控制台中看到“取出垃圾 1”以及页面上的相同文本。而不是这个,我按预期在控制台中看到“取出垃圾 1”,但在页面上我看到“取出垃圾 11”,然后看到“取出垃圾 1111”,因为这段代码可以双倍工作,但是为什么在控制台中我看到第一个工作的唯一结果:“取出垃圾1”,“取出垃圾111”?

是的,我知道这样更改项目是不正确的,我应该这样做

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

如果我这样写 - 一切正常,而且这段代码还有其他缺陷,实际上这段代码不是我的,但我只是想知道这个魔法是如何发生的:

  1. 为什么它会工作两次
  2. 为什么我在控制台中只看到第一个输出

沙盒示例 https://codesandbox.io/s/objective-margulis-0phvx?file=/src/App.js

【问题讨论】:

  • 我认为这是因为todo.text += 1 在将 int 添加到字符串时,它会追加而不是增加字符串中的数字。如果您想增加,则必须单独增加数量并附加(这就是您想要做的)
  • 这实际上正在发生,因为您正在使用函数设置状态。使用this.setState({ todos: [] }) 并使用this.state.todos 而不是prevState.todos。我有点困惑为什么会这样。
  • 对不起,伙计们,我无话可说 - 你的回答并没有让情况更清楚 =)

标签: javascript reactjs setstate


【解决方案1】:

这是一个有趣的问题。您可以尝试使用一个字母(或任何字符串),同样的事情也会发生。你也可以在开头附加一个字符串,同样的事情也会发生。

例如

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

我完全没有受过教育的猜测是,在 React 内部的某个地方,它会进行某种字符串比较并找出不同之处;并且可能您的代码修改了原始对象中的字符串,并且在应用它时,它会将其应用到已经修改的字符串。

但是……如果我这样做:

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

字符串的 t 部分不会保持不变。

有趣的是,如果你这样做:

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

确实发出两次警报(尽管正如您所提到的,它只有一次 console.logs)。

所以看起来这段代码出于某种原因确实运行了两次。我不知道为什么只有一条 console.log 消息。

【讨论】:

    猜你喜欢
    • 2021-09-20
    • 2022-08-05
    • 2020-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多