React Hooks 显示不一致的行为

发布于 2025-01-11 01:00:17 字数 648 浏览 0 评论 0原文

我想感受一下反应钩子,并通过制作一个带有添加和删除功能的普通待办事项列表来尝试它们,但我遇到了一种恼人的行为,而且我不知道其背后的逻辑。

 const handleDeleteTodos = (event) => {
    todos.splice(event.target.name, 1);
    setTodos(todos)
    window.localStorage.setItem('Todos', JSON.stringify(todos));
  };

所以上面的代码确实删除了该元素,我知道这一点是因为当我重新加载页面时,该元素就消失了。但它不会像添加待办事项功能那样重新渲染。虽然下面的代码有效。

const handleDeleteTodos = (event) => {
    const newTodos = [...todos];
    newTodos.splice(event.target.name, 1); 
    setTodos(newTodos)
    window.localStorage.setItem('Todos', JSON.stringify(newTodos));
  };

我看到的唯一区别是代码的对象解构方面。

我只是想知道这种行为背后的逻辑是什么。

I wanted to get a feel for react hooks and was trying them out by making a normal todo list with adding and removing capabilities but I have come across an annoying behaviour and I do not know the logic behind it.

 const handleDeleteTodos = (event) => {
    todos.splice(event.target.name, 1);
    setTodos(todos)
    window.localStorage.setItem('Todos', JSON.stringify(todos));
  };

So the code above does delete the element, I know this because when I reload the page, the element is gone. But it doesn't re-render like the add todo function. While the code below works.

const handleDeleteTodos = (event) => {
    const newTodos = [...todos];
    newTodos.splice(event.target.name, 1); 
    setTodos(newTodos)
    window.localStorage.setItem('Todos', JSON.stringify(newTodos));
  };

I only difference I see is the object destructuring aspect of the code.

I just wanted to know what is the logic behind this behaviour.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

居里长安 2025-01-18 01:00:17

您看到 过时的道具

使用setState的函数形式修改对象时无需注意(因为对该对象的引用是由 React 传递给您的)。

const handleDeleteTodos = (event) => {
  const { name } = event.target; // important to copy the name before `setTodos`, since react reuses events
  setTodos((todos) => {
    const newTodos = [...todos];
    newTodos.splice(name, 1);
    return newTodos;
  });
};

另外,使用 useEffect 基于 todos 修改 localStorage,因为它实际上是修改状态的副作用,而不是手动执行各个修改功能。

useEffect(() => {
  window.localStorage.setItem('Todos', JSON.stringify(todos));
}, [todos]);

You're seeing stale props.

Use the function form of setState to modify objects without that caveat (since the reference to that object is passed to you by React).

const handleDeleteTodos = (event) => {
  const { name } = event.target; // important to copy the name before `setTodos`, since react reuses events
  setTodos((todos) => {
    const newTodos = [...todos];
    newTodos.splice(name, 1);
    return newTodos;
  });
};

Also, use useEffect to modify localStorage based on todos, since it literally is a side effect of state being modified, instead of doing it manually in each modification function.

useEffect(() => {
  window.localStorage.setItem('Todos', JSON.stringify(todos));
}, [todos]);
葬花如无物 2025-01-18 01:00:17

仅当对状态对象的引用发生更改(或者如果它是标量,则值本身)时,React 才会重新渲染。
您必须创建一个新对象并将其设置为状态。

第一个示例使用相同的引用,因此 React 不知道有什么变化。
第二个示例使用新的引用。

React only rerenders if the reference to the object on state has changed (or if it's scalar, the value itself).
You must create a new object and set it on state.

The first example uses the same reference, so React has no idea anything changed.
The second example uses a new reference.

回忆凄美了谁 2025-01-18 01:00:17

问题

Array.prototype.splice 对数组进行就地突变

Array.prototype.splice

splice() 方法通过删除或删除数组来更改数组的内容
替换现有元素和/或添加新元素就地
要访问数组的一部分而不修改它,请参阅 slice()

您正在改变当前状态,然后将相同的引用保存回状态。

const handleDeleteTodos = (event) => {
  todos.splice(event.target.name, 1); // <-- mutation
  setTodos(todos); // <-- same state reference
  window.localStorage.setItem('Todos', JSON.stringify(todos));
};

因为永远不会创建新的数组引用,所以 React 不会看到状态已更新。

解决方案

创建新的数组引用允许 React 使用浅引用相等性检查和重新渲染。

const handleDeleteTodos = (event) => {
  const newTodos = [...todos]; // <-- shallow copy into new reference
  newTodos.splice(event.target.name, 1); // <-- mutate copy
  setTodos(newTodos); // <-- save new reference into state

  window.localStorage.setItem('Todos', JSON.stringify(newTodos));
};

更传统的方法是使用Array.prototype.filter浅复制数组并同时删除元素

const handleDeleteTodos = (event) => {
  const newTodos = todos.filter((_, index) => event.target.name !== index);

  setTodos(newTodos);

  window.localStorage.setItem('Todos', JSON.stringify(newTodos));
};

Issue

Array.prototype.splice does an in-place mutation of the array.

Array.prototype.splice

The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place.
To access part of an array without modifying it, see slice()

You are mutating the current state and then saving the same reference back into state.

const handleDeleteTodos = (event) => {
  todos.splice(event.target.name, 1); // <-- mutation
  setTodos(todos); // <-- same state reference
  window.localStorage.setItem('Todos', JSON.stringify(todos));
};

Because a new array reference is never created, React doesn't see that state was ever updated.

Solution

Creating a new array reference allows React to use a shallow reference equality check and rerender.

const handleDeleteTodos = (event) => {
  const newTodos = [...todos]; // <-- shallow copy into new reference
  newTodos.splice(event.target.name, 1); // <-- mutate copy
  setTodos(newTodos); // <-- save new reference into state

  window.localStorage.setItem('Todos', JSON.stringify(newTodos));
};

A more conventional method is to use Array.prototype.filter to shallow copy the array and remove the element at the same time.

const handleDeleteTodos = (event) => {
  const newTodos = todos.filter((_, index) => event.target.name !== index);

  setTodos(newTodos);

  window.localStorage.setItem('Todos', JSON.stringify(newTodos));
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文