如何在不更改数组的情况下更新此状态?

时间:2020-02-14 16:32:47

标签: javascript reactjs react-hooks

在以下情况下,我想知道如何突变状态:

我正在使用React Hooks。我有一个列出TodoList的{​​{1}}。可以单击TodoItems来运行一个名为TodoItems的函数,该函数可以切换其toggle属性。

completed

有人告诉我这是变异状态:

您创建了todos数组的副本,但是仍然对数组中的项进行了变异:

import React, { useState } from "react";
import TodoItem from "./TodoItem";

export default function TodoList() {
  const [todos, setTodos] = useState([
    { text: "Example", completed: false }
  ]);

  const toggle = index => {
    const newTodos = [...todos];
    newTodos[index].completed = !newTodos[index].completed; // Mutating state?
    setTodos(newTodos);
  };

  return (
    <>
      {todos.map((item, index) => (
        <TodoItem key={index} index={index} todo={item} toggle={toggle} />
      ))}
    </>
  );
}

散布数组只会创建该数组的浅表副本,但是原始的todo对象不会被复制并因此发生变异。

那么,在不改变状态的情况下处理此问题的正确方法是什么?我尝试传递newTodos[index].completed = !newTodos[index].completed; 参数,但是状态不会更新:

prevTodos

更新:我查看了您所有推荐的帖子。这是一个解决方案,尽管我想知道是否有一种更简洁的表达方式:

const toggle = index => {
  setTodos(prevTodos => {
    prevTodos[index].completed = !prevTodos[index].completed;
    return prevTodos;
  });
};

更新2:谢谢大家。我在下面发布了解决方案。 StackOverflow不允许我接受2天,但是我会做。在建议的帖子上接受的答案建议使用第三方库,并且不使用React Hooks,因此我不认为这个问题是重复的。

3 个答案:

答案 0 :(得分:0)

找到了解决方案。 map将返回一个不同的数组,您可以侦听index参数(在下面的代码段中为i),以侦听当前索引并更改您要定位的对象。

const toggle = index => {
  setTodos(
    todos.map((todo, i) =>
      i === index ? { ...todo, completed: !todo.completed } : todo
    )
  );
};

这些线程帮助我将它们拼凑在一起:

感谢所有提供帮助的人。

答案 1 :(得分:0)

是的,避免此问题的最简单方法是avoid mutating data

做到这一点的一种方法是使用功能性方法,特别是使用.map这样的非变异数组方法:

function TodoList() {
  const [todos, setTodos] = React.useState([{
    text: "Example",
    completed: true
  }]);

  const toggle = index => {
    const updatedTodos = todos.map((todo, i) => {
      if (i === index) {
        todo.completed = !todo.completed;
      }
      return todo;
    });
    setTodos(updatedTodos);
  };

  return (
    <React.Fragment > 
      {todos.map((item, index) => (
        <p
          key={item.text}
          onClick={() => toggle(index)}
          className={item.completed ? "completed" : ""}
        >
          {item.text}
        </p>
       ))}
    </React.Fragment>
  );
}

ReactDOM.render( < TodoList / > , document.getElementById('root'))
.completed {
  text-decoration: line-through;
}
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 2 :(得分:-3)

const toggle = (index) => {
  const oldTodo = todos[index];
  setTodos(oldState => {
   oldState.splice(index, 1, {...oldTodo, checked: !oldTodo.checked})

   return oldState
})
}