簡體   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