【问题标题】:react.js event handler is not returning updated datareact.js 事件处理程序未返回更新的数据
【发布时间】:2021-09-26 14:31:43
【问题描述】:

我是 react.js 的新手。我正在关注 youtube 上的视频以学习 react.js。我正在处理简单的事件处理并陷入了一些问题。当用户在复选框上执行 onclick 功能时,我想选中/取消选中该复选框。但不知何故,返回的数组没有更新,复选框是不可操作的。我在下面粘贴我的代码:

App.js

import React from 'react'
import Header from './components/Header'
import Todolist from './components/Todolist'
import todosData from './data/todosData'

class App extends React.Component {

  constructor(){
    super()
    this.state = {
      todos: todosData
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(id){
    this.setState(prevState => {
        console.log(prevState.todos)
        const updatedTodos = prevState.todos.map(todo => {
          if(todo.id === id){
            todo.completed = !todo.completed
          }
          return todo
        })
        console.log(updatedTodos)
        return{
          todos : updatedTodos
        }
    })
  }

  render(){
    const todoItems = this.state.todos.map(item => <Todolist key={item.id} item={item} handleChange={this.handleChange} />)
    return (
      <div>
        <Header />
        {todoItems}
      </div>
    )
  }

}

export default App;

TodoList.js

import React from 'react'

function Todolist(props){
    return(
        <div className='todo-item'>
            <input type='checkbox'
                   checked={props.item.completed} 
                   onChange={() => props.handleChange(props.item.id)} 
            />
            <span>{props.item.text}</span>
        </div>
        )
}

export default Todolist

【问题讨论】:

  • 您的Todolist 组件实际上应该命名为TodoTodoItem,因为它代表一个单独的Todo 项。不正确的命名会影响理解并最终可能导致错误。
  • 能否请您发布您的控制台的屏幕截图。
  • 我复制了你的代码,它工作得很好。不知道你有什么问题:stackblitz.com/edit/react-zee3sl?file=src%2FApp.js
  • 奇怪的是代码在你的端工作。可能是环境差异,也可能是您没有使用严格模式。
  • 奇怪的是代码在你的端工作。可能是环境差异,也可能是您没有使用严格模式。

标签: reactjs react-hooks


【解决方案1】:

您正试图在这一行改变数组的原始项目/对象!

todo.completed = !todo.completed

您可以尝试使用Object.assign 或使用spread 创建一个新对象。两者都很好,但传播方式更可取。

使用Object.assign

  handleChange(id) {
    this.setState((prevState) => {
      const updatedTodos = prevState.todos.map((todo) => {
        // Object.assign creates a new object!
        const changedTodo = Object.assign({}, todo, {
          completed: todo.id === id ? !todo.completed : todo.completed
        });

        return changedTodo;
      });
      return {
        todos: updatedTodos
      };
    });
  }

使用传播...

handleChange(id) {
    this.setState((prevState) => {
      const updatedTodos = prevState.todos.map((todo) => {
        const changedTodo = {
          ...todo,
          completed: todo.id === id ? !todo.completed : todo.completed
        };

        return changedTodo;
      });
      return {
        todos: updatedTodos
      };
    });
  }

【讨论】:

  • 很棒的回复,兄弟。但我在这里有一个困惑。我们不是在下面的行中改变内部的原始对象吗? todos: updatedTodos 在这里我们完全更新了原始对象。不是突变吗?
  • 当你从 setState 返回时,React 正在处理。我们不需要将其他属性与返回对象一起传递。 state 是可变的,props 是不可变的。
【解决方案2】:

here 所述,setStatestrict mode 中运行两次。状态更新函数被调用两次,输入相同。这应该没问题并产生相同的输出。但这里的问题是您重用了todo 对象,而不是创建一个新对象。这反过来会导致completed 标志被翻转两次。

你可以改变你的更新函数,每次都创建一个新的 todo 对象:

this.setState(prevState => {
  const updatedTodos = prevState.todos.map(todo => {
    if (todo.id === id) {
      return {
        ...todo,
        completed: !todo.completed,
      };
    }
    return todo;
  });
  return {
    todos: updatedTodos,
  };
});

【讨论】:

  • 谢谢大哥的回复。它完美地工作。但我在这里有一个困惑。我们不是在下面的行中改变内部的原始对象吗? todos: updatedTodos 在这里我们完全更新了原始对象。不是突变吗?
猜你喜欢
  • 2016-03-09
  • 2022-06-29
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
相关资源
最近更新 更多