【问题标题】:Cannot map through state using ES6 in a ToDo App using React?无法在使用 React 的 ToDo 应用程序中使用 ES6 映射状态?
【发布时间】:2020-12-22 04:14:14
【问题描述】:

我正在开发一个简单的 ToDo 应用程序,目前我正忙于更新我的 App.js 组件的状态。

这是我的 App.js:

import React, { Component } from "react";
import TodoItem from "./ToDoItem";
import todosData from "./ToDoData";

class App extends Component {
  constructor() {
    super();
    this.state = {
      loadedData: todosData,
    };
  }

  newChange = (id) => {
    const update = this.state.loadedData.map((todo) => {
      if (todo.id === id) {
        todo.completed = !todo.completed;
      }
      return todo;
    });
    // this is where my update variable will not update?
    console.log(update);
    this.setState({ loadedData: update });
  };

  render() {
    const todoComponent = this.state.loadedData.map((item) => (
      <TodoItem
        key={item.id}
        text={item.text}
        completed={item.completed}
        newChange={this.newChange}
      />
    ));

    return <div className="todo-list">{todoComponent}</div>;
  }
}

export default App;

这是我的 TodoItem 组件(我猜一旦此状态正确传递,它就会正常工作):

import React from "react";

class TodoItem extends React.Component {
  render() {
    return (
      <div className="todo-item">
        <input
          type="checkbox"
          checked={this.props.completed}
          onChange={() => this.props.newChange(this.props.id)}
        />
        <p>{this.props.text}</p>
      </div>
    );
  }
}

export default TodoItem;

最后,我的数据只是一个由对象组成的数组:

    const todosData = [
  {
      id: 1,
      text: "Take out the trash",
      completed: true
  },
  {
      id: 2,
      text: "Grocery shopping",
      completed: false
  },
  {
      id: 3,
      text: "Clean gecko tank",
      completed: false
  },
  {
      id: 4,
      text: "Mow lawn",
      completed: true
  },
  {
      id: 5,
      text: "Catch up on Arrested Development",
      completed: false
  }
]

export default todosData

我最大的问题是为什么我的 newChange() 方法中的 update 变量在我映射时没有得到更新?

当我 console.log 它显示完全相同的数据并且我想要的属性没有得到更新?

loadedData 仍然与 update 完全相同。

【问题讨论】:

    标签: javascript reactjs ecmascript-6 array.prototype.map


    【解决方案1】:

    Danny,您将console.log(update) 放在那里是正确的。这样你发现函数实际上并没有更新map函数中的现有状态。

    要找出原因,您需要检查 map 函数内部发生了什么,第一步是测试您在其中使用的所有内容。在这种情况下,您使用的是this.props.id,您可以通过放置 console.log 来检查它的值:

      newChange = id => {
        console.log("id:", id); // NEW
        const update = this.state.loadedData.map(todo => {
          if (todo.id === id) {
            todo.completed = !todo.completed;
          }
          return todo;
        });
        // this is where my update variable will not update?
        console.log(update);
        this.setState({ loadedData: update });
      };
    

    好吧,在这种情况下,当您尝试单击待办事项时,它将打印id: undefined,因为您实际上并没有将它作为道具传递。 map 函数循环遍历待办事项,但从未找到任何满足 if (todo.id === id) 的待办事项(因为它在您的情况下转换为 if (todo.id === undefined)

    修复很简单,只需将id 作为道具传递给todoComponent

      const todoComponent = this.state.loadedData.map(item => (
          <TodoItem
            id={item.id} // NEW
            key={item.id}
            text={item.text}
            completed={item.completed}
            newChange={this.newChange}
          />
        ));
    

    【讨论】:

    • 感谢您的详细回复!这在概念层面上非常清楚! :)
    【解决方案2】:

    在 App.js 中创建 ToDoItems 时,您只是缺少一个道具 (id)

    您尝试在 ToDoItem 中访问它,但您从未设置它...

    所以你在 App 中的渲染函数应该是这样的:

    render() {
        const todoComponent = this.state.loadedData.map((item) => (
          <TodoItem
            key={item.id}
            id={item.id}
            text={item.text}
            completed={item.completed}
            newChange={this.newChange}
          />
        ));
    
        return <div className="todo-list">{todoComponent}</div>;
      }
    

    【讨论】:

    • 哇,非常感谢。我不敢相信我只是假设 .id 值只是来自子组件的键设置。谢谢你UUUUU!!!!
    • 当然,不客气!但是,是的,这很令人困惑,关键实际上甚至不是道具
    【解决方案3】:

    您在执行时意外地改变了 newChange 处理程序中的状态:

    todo.completed = !todo.completed;
    

    相反,创建一个全新的 state 对象切片:

    newChange = (id) => {
      this.setState({
        loadedData: this.state.loadedData.map((todo) => ({
          ...todo,
          completed: todo.id === id ? !todo.completed : todo.completed,
        })),
      });
    };
    

    或者,如果你不能使用扩展运算符 (...),试试这个:

    newChange = (id) => {
      this.setState({
        loadedData: this.state.loadedData.map((todo) =>
          Object.assign({}, todo, {
            completed: todo.id === id ? !todo.completed : todo.completed,
          })
        ),
      });
    };
    

    您可以在“The Power Of Not Mutating Data”下的官方 React 文档中阅读更多内容

    【讨论】:

    • 很遗憾,这是一个错误的答案。这里的可变性没有问题,因为 map 用于创建一个新数组。问题是 id 实际上并没有作为道具发送,导致映射函数内部的 todo.id === undefined 比较永远不会评估为 true
    • 非常感谢。这很有道理!!!感谢您深思熟虑的回应 :) @ivanD 是的,我现在明白了,但是不修改原始状态是否仍然正确?
    • 啊,明白了。我认为即使在那里突变也不安全,因为对象是通过引用分配的。发现问题的好方法!
    • 避免改变你的状态总是一个好主意,Reed 是对的。这不是你得到错误的原因:)
    猜你喜欢
    • 1970-01-01
    • 2019-12-03
    • 2018-05-07
    • 2019-05-05
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 2016-04-13
    • 1970-01-01
    相关资源
    最近更新 更多