【问题标题】:React hooks: Issue with React.memo and useStateReact 钩子:React.memo 和 useState 的问题
【发布时间】:2019-08-18 11:14:43
【问题描述】:

我玩过 react hooks,遇到了一个我不明白的问题。

代码在这里https://codesandbox.io/embed/hnrch-hooks-issue-dfk0t 该示例有 2 个简单的组件:

应用组件

const App = () => {
  const [num, setNum] = useState(0);
  const [str, setStr] = useState();

  const inc = () => {
    setNum(num + 1);
    setStr(num >= 5 ? ">= 5" : "< 5");
    console.log(num);
  };

  const button = <button onClick={inc}>++</button>;

  console.log("parent rerender", num);

  return (
    <div className="App">
      <h1>App</h1>
      <Child str={str}>{button}</Child>
    </div>
  );
};

子组件

const Child = React.memo(
  ({ str, children }) => {
    console.log("child rerender");
    return (
      <div>
        <>
          <h2>Functional Child</h2>
          <p>{str}</p>
          {children}
        </>
      </div>
    );
  }
  (prev, props) => {
    return prev.str === props.str;
  }
);

所以我将孩子包裹在 React.memo 中,并且只有在 str 不同时才应该重新渲染。但是它也有孩子,并且它传递了一个按钮,该按钮正在增加父级(App)内的计数器。

问题是:计数器设置为 1 后将停止递增。有人可以向我解释这个问题以及我需要了解什么以避免这些错误吗?

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    这是一个“关闭问题”。

    这是App的第一次渲染时间,inc函数第一次创建:(我们称之为inc#1)

    const inc = () => {
      setNum(num + 1);
      setStr(num >= 5 ? ">= 5" : "< 5");
      console.log(num);
    };
    
    

    在inc#1 范围内,num 当前为0。然后将该函数传递给button,然后将其传递给Child。

    到目前为止一切顺利。现在你按下按钮,inc#1 被调用,这意味着

    setNum(num + 1);
    

    num === 0 发生的地方。 App 被重新渲染,但是 Child 不是。条件是如果prev.str === props.str 然后我们不会再次渲染 Child。

    我们现在处于App 的第二次渲染中,但Child 仍然拥有inc#1 实例,其中num 是0。

    您知道问题出在哪里了吗?您仍将调用该函数,但 inc 现在已过时。

    您有多种方法可以解决问题。您可以确保Child 始终拥有更新的道具。

    或者您可以将回调传递给setState 以获取当前值(而不是位于闭包范围内的陈旧值)。这也是一种选择:

    const inc = () => {
      setNum((currentNum) => currentNum + 1);
    };
    
    React.useEffect(() => {
      setStr(num >= 5 ? ">= 5" : "< 5");
    }, [num])
    

    【讨论】:

      【解决方案2】:

      这里有几件事。

      1. 如果你正在修改一个状态并且它的新值依赖于之前的状态值,使用setState的函数形式:
       setNum(num => num + 1);   
      
      1. setState 是异步的,因此当您尝试 setStr 时,num 值尚未更新。更重要的是,在您的特定情况下,inc 正在关闭(即创建一个闭包)num 来自状态的值,因此在该函数内部它将始终具有其初始值 - 0。 要解决此问题,您需要在 num 更改时使用 Effect 挂钩更新字符串:
      useEffect(() => {
          setStr(num >= 5 ? ">= 5" : "< 5");
        }, [num]) // Track the 'num' var here
      

      【讨论】:

        【解决方案3】:

        修复该错误的一种方法是将inc 函数更改为:

        const inc = () => {
          setNum(n => {
            const newNum = n + 1;
            setStr(newNum >= 5 ? ">= 5" : "< 5");
            return newNum;
          });
        };
        

        请注意,setState 现在传递了一个回调函数,该函数接收旧值并返回新值。这样,“关闭”问题就解决了。

        【讨论】:

          猜你喜欢
          • 2021-02-07
          • 2020-04-29
          • 1970-01-01
          • 2021-04-04
          • 2023-03-19
          • 1970-01-01
          • 2020-04-10
          • 1970-01-01
          • 2021-05-25
          相关资源
          最近更新 更多