React setState 在直接改变状态时不起作用,为什么不呢?

huangapple go评论70阅读模式
英文:

React setState doesn't work when mutating the state directly why not

问题

以下是您要翻译的内容:

[Code](https://codesandbox.io/s/new-http-99jekw), i am trying to copy the list as another list and modifying the name property, I was wondering why would this not work, i know that shallow copying this works/ also when i use a fn updater it works , thanks

import { useState } from "react";

const initList = { name: "abhi" };

export default function List() {
  const [list, setList] = useState(initList);

  function handleClick() {
    const nextList = list;
    nextList.name = "anand";
    // setList((list) => ({ ...list }));
    setList(nextList);
  }

  return (
    <>
      <button onClick={handleClick}>change name</button>
      <ul>{list.name}</ul>
    </>
  );
}

编辑:我理解了这是什么以及如何实现,想知道为什么在Reactjs中会发生这种情况。我已经得到了答案,非常感谢各位帮助我。

英文:

Code, i am trying to copy the list as another list and modifying the name property, I was wondering why would this not work, i know that shallow copying this works/ also when i use a fn updater it works , thanks


    import { useState } from &quot;react&quot;;
    
    const initList = { name: &quot;abhi&quot; };
    
    export default function List() {
      const [list, setList] = useState(initList);
    
      function handleClick() {
        const nextList = list;
        nextList.name = &quot;anand&quot;;
        // setList((list) =&gt; ({ ...list }));
        setList(nextList);
      }
    
      return (
        &lt;&gt;
          &lt;button onClick={handleClick}&gt;change name&lt;/button&gt;
          &lt;ul&gt;{list.name}&lt;/ul&gt;
        &lt;/&gt;
      );
    } 

Edit: I understand the what and how, wanted to know why this happens in Reactjs. I have got my answer, thank you so much guys for helping me out

答案1

得分: 3

这里是你要翻译的代码部分:

// Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
  markWorkInProgressReceivedUpdate();
}

你可以在这里找到is函数的定义。重要的部分是它通过引用比较了两个传递的对象(旧状态和新状态)。由于你没有创建新状态的浅拷贝,它们实际上是相同的,因此不会进行更新。

也许有趣的是,使用setState的函数版本并不会自动阻止你犯同样的错误。它也必须返回与旧状态不同的引用。换句话说,以下代码也不会更新状态:

const nextList = list;
nextList.name = "anand";
setList((list) => nextList);
英文:

You've got answers already but to be precise here's the piece of source code from react repo that I believe is responsible for what you're seeing:

// Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
  markWorkInProgressReceivedUpdate();
}

You can find the definition of is function here. The important bit is that it compares two passed objects (old state and a new state) by reference. Since you did not make a shallow copy of the new state these are in fact the same and no update is made.

Perhaps an interesting thing is that using functional version of setState does not automatically save you from the same mistake. It also has to return a different reference from the old state.
In other words, this also won't update state:

const nextList = list;
nextList.name = &quot;anand&quot;;
setList((list) =&gt; nextList);

答案2

得分: 0

const nextList = list 不会创建一个新对象,它会创建一个变量 nextList,指向与 list 相同的对象。

因此,当你改变 nextList 引用的对象时,你也在改变 list 引用的同一个对象。

这里可以了解更多信息。

正如你所说,你已经知道在 React 中不应该改变状态

英文:

const nextList = list will not create a new object, it will create a variable nextList that points to the same object as list.

Consequently, when you mutate the object referenced by nextList, you're mutating the same object referenced by list.

Read more here.

And, as you stated, you already know state in react shouldn't be mutated.

答案3

得分: 0

我没有完全理解你的问题,但你想要保持对象的初始值 { name: "abhi" },并在 nextList 常量上将 name 更改为 "anand",对吗?像这样得到:initList = { name: "abhi" }list = { name: "anand" }?如果是这样,只需这样做:

import { useState } from "react";

const initList = { name: "Test 1" };

export default function List() {
  const [list, setList] = useState({ ...initList });

  function handleClick() {
    const nextList = list;
    nextList.name = "Test 2";
    // setList((list) => ({ ...list }));
    setList(nextList);
  }

  return (
    <>
      <button onClick={handleClick}>change name</button>
      <ul>{list.name}</ul>
      <ul>{initList.name}</ul>
    </>
  );
}

这是因为你将对象及其指针插入到 list 的状态中,而在这种情况下,你只需要对象的初始值而不是整个对象。

英文:

I didn't quite understand your question, but you would like to keep the object's initial value { name: &quot;abhi&quot; } and on const nextList change name to "anand", right? getting like this: initList = { name: &quot;abhi&quot; } and list = { name: &quot;anand&quot; }? If so, just do this:

import { useState } from &quot;react&quot;;

const initList = { name: &quot;Test 1&quot; };

export default function List() {
  const [list, setList] = useState({ ...initList });

  function handleClick() {
    const nextList = list;
    nextList.name = &quot;Test 2&quot;;
    // setList((list) =&gt; ({ ...list }));
    setList(nextList);
  }

  return (
    &lt;&gt;
      &lt;button onClick={handleClick}&gt;change name&lt;/button&gt;
      &lt;ul&gt;{list.name}&lt;/ul&gt;
      &lt;ul&gt;{initList.name}&lt;/ul&gt;
    &lt;/&gt;
  );
} 

This happens because you are inserting the object and its pointer to the state of the list, in this case what you need is just the initial values ​​of the object and not the complete one.

huangapple
  • 本文由 发表于 2023年2月10日 02:51:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403167.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定