繁体   English   中英

将对象附加到数组,为什么“推送”不起作用?

[英]Append an object to an array, why does "push" not work?

我只是想知道为什么线33不起作用(标注出来),但32行确实工作。 为什么我不能简单地将对象推送到数组? 这两行(第 32 行和第 33 行)不应该做完全相同的事情吗?

参考: https : //codesandbox.io/s/lingering-wood-33vtb?file=/src/ App.js: 0-927

import React, { useState } from "react";

function ThingsToDo({ thing }) {
  return <div>{thing.text}</div>;
}

function ToDoForm({ add }) {
  const [value, setValue] = useState("");

  const updateKey = (e) => {
    e.preventDefault();
    if (!value) return;
    add(value);
    setValue("");
  };

  return (
    <form onSubmit={updateKey}>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
    </form>
  );
}

function App() {
  const [todos, setTodos] = useState([{ text: "Hi" }, { text: "Bye" }]);

  const Addtolist = (text) => {
    const newToDo = [...todos, { text }];
    //     const newToDo = todos.push({text})
    setTodos(newToDo);
  };

  return (
    <div>
      {todos.map((todo, index) => (
        <div>
          <ThingsToDo thing={todo} />
        </div>
      ))}
      <ToDoForm add={Addtolist} />
    </div>
  );
}

export default App;

你不能在反应中直接改变状态。 在您的两种情况下:

    const newToDo = [...todos, { text }];

这将创建 todos 数组的副本并添加您的额外项目。 newTodo 是一个副本,然后当您 setTodos(newTodo) 时,您将状态设置为新复制的数组。

 const newToDo = todos.push({text})

这个试图将 {text} 推入 todos 这是一种状态。 你不能直接改变状态,你必须通过 setTodos() 来做到这一点。

如果你真的开始使用推送,那么你可以先复制 todos 的状态,然后推送到复制的数组中,但我认为这是额外的不必要的工作。

你必须做如下,

// const newToDo = [...todos, { text }];
const newToDo = JSON.parse(JSON.stringify(todos));
newToDo.push({ text });
setTodos(newToDo);

原因:

  1. 您正在尝试改变状态变量 todos(不起作用),您必须使用 setState
  2. 如果你真的想改变并设置新的状态,你必须使用 JSON.parse, JSON.strigify 进行克隆,否则你无法创建真正的克隆

正如官方 React 文档https://reactjs.org/docs/react-component.html所说,

永远不要直接改变 this.state,因为之后调用 setState() 可能会替换你所做的改变。 将 this.state 视为不可变的。

您试图将一个对象推送到 React 状态数组中,这违反了不变性。 要更新反应状态,您肯定必须使用setState (在您的情况下是setTodos因为您正在构建功能组件)。

另一件事是,当您推送数组并将其分配给 const 变量时,它将分配数组的长度而不是您想要的数组。

正如 w3school 页面在https://www.w3schools.com/jsref/jsref_push.asp 中所说

push() 方法将新项添加到数组的末尾,并返回新长度。

我解决这个问题的替代方法是使用Array类型的.concat方法,因为它不会改变原始数组,而只会返回一个带有连接对象的新数组。

例子。

setTodos(todos.concat(text));

参考:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

第一种情况

那是行不通的。 要触发状态更改,您必须使用setState来更新状态。 使用todo.push({ text })它将更新状态但永远不会触发状态更改。

第二种情况

push返回您添加到数组中的项目。 因此,在 setState 中,您尝试使用{ text : "somText" }覆盖当前状态数组。 所以现在在你的渲染方法中你不能通过状态数组进行映射,你会得到一个错误。

第三种情况

您需要了解数组是引用类型。 所以即使你做了类似下面的事情,它也会直接更新 todo 数组,因为这不是一个真正的副本。

let copy = todo;
copy.push({text});
setTodo(copy)

有两种方法可以复制。

  1. 浅拷贝
  2. 深拷贝

在浅拷贝中,您只复制值,原型仍将引用原始对象。

在深度复制中,它复制所有内容。 甚至参考文献也被复制。

在您的情况下,浅拷贝足以执行状态更新。 你可以通过[...todos, {text}]做浅拷贝

在 JavaScript 中,如果您需要进行深度复制,一个简单的方法是,

const deepCpy = JSON.parse(JSON.stringiy(todos))

您也可以在lodash使用 deepCopy 方法

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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