React hooks:React.memo 和 useState 的问题 [英] React hooks: Issue with React.memo and useState

查看:41
本文介绍了React hooks:React.memo 和 useState 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试了 React 钩子,但遇到了一个我不明白的问题.

代码在这里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");控制台日志(数量);};const button = <button onClick={inc}>++</button>;console.log("父重新渲染", num);返回 (<div className="应用程序"><h1>应用</h1><Child str={str}>{button}</Child>

);};

子组件

const Child = React.memo(({ str, children }) =>{console.log("子重新渲染");返回 (<div><><h2>功能儿童</h2><p>{str}</p>{孩子们}</>

);}(上一个,道具)=>{return prev.str === props.str;});

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

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

解决方案

这是一个关闭问题".

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

const inc = () =>{setNum(num + 1);setStr(num >= 5 ? ">= 5" : "< 5");控制台日志(数量);};

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

到目前为止一切都很好.现在你按下按钮,inc#1 被调用,这意味着

setNum(num + 1);

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

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

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

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

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

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

I played around with react hooks, and I came across an issue that I do not understand.

The code is here https://codesandbox.io/embed/hnrch-hooks-issue-dfk0t The example has 2 simple components:

App Component

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>
  );
};

Child Component

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;
  }
);

So I have the child wrapped in a React.memo, and it should only rerender if str is different. But is also has children, and it get's passed a button which is incrementing a counter inside the parent (App).

The issue is: The counter will stop incrementing after it is set to 1. Can someone explain this issue to me and what I need to understand to avoid those bugs?

解决方案

It's a "closure issue".

This is the first render time for App, the inc function has been created the first time: (let's call it inc#1)

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

In the inc#1 scope, num is currently 0. The function is then passed to button which is then passed to Child.

All good so far. Now you press the button, inc#1 is invoked, which mean that

setNum(num + 1);

where num === 0 happen. App is re-rendered, but Child is not. The condition is if prev.str === props.str then we don't render again Child.

We are in the second render of App now, but Child still own the inc#1 instance, where num is 0.

You see where the problem is now? You will still invoke that function, but inc is now stale.

You have multiple ways to solve the issue. You could make sure that Child has always the updated props.

Or you could pass a callback to setState to fetch the current value ( instead of the stale one that live in the scope of the closure ). This is also an option:

const inc = () => {
  setNum((currentNum) => currentNum + 1);
};

React.useEffect(() => {
  setStr(num >= 5 ? ">= 5" : "< 5");
}, [num])

这篇关于React hooks:React.memo 和 useState 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆