【问题标题】:React Hooks render twiceReact Hooks 渲染两次
【发布时间】:2021-09-09 00:08:09
【问题描述】:

我定义了一个场景:我们有一个使用父级道具和自身状态的组件。

下面有两个组件DC和JOKER以及我的步骤:

  1. 点击 DC 的按钮
  2. DC 设置计数
  3. JOKER 将使用旧状态进行渲染
  4. 运行 useEffect 和 setCount
  5. JOKER 再次渲染

我想问一下为什么 JOKER 渲染两次(第 3 步和第 5 步)而第一次渲染会浪费性能。 我只是不想第 3 步。 如果在类组件中,我可以使用 componentShouldUpdate 来避免它。但是 Hooks 也有同样的东西?

我的代码在下面,或者打开这个网站https://jsfiddle.net/stephenkingsley/sw5qnjg7/

import React, { PureComponent, useState, useEffect, } from 'react';

function JOKER(props) {
  const [count, setCount] = useState(props.count);
  useEffect(() => {
    console.log('I am JOKER\'s useEffect--->', props.count);
    setCount(props.count);
  }, [props.count]);

  console.log('I am JOKER\'s  render-->', count);
  return (
    <div>
      <p style={{ color: 'red' }}>JOKER: You clicked {count} times</p>
    </div>
  );
}

function DC() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => {
        console.log('\n');
        setCount(count + 1);
      }}>
        Click me
      </button>
      <JOKER count={count} />
    </div>
  );
}

ReactDOM.render(<DC />, document.querySelector("#app"))

【问题讨论】:

  • 因为您正在使用不会重新渲染的setCount(props.count); 行删除更新 JOKER 本地状态。更多信息reactjs.org/docs/hooks-effect.html
  • @ReyanshMishra 我担心第 3 步。我知道当我在 JOKER 中设置计数时,JOKER 会再次渲染。如果在类组件中,我可以使用 componentShouldUpdate 来避免它。现在 hooks 有同样的东西还是 api?
  • useEffect 设置为在第一次渲染之后运行,以便开发人员在 effect 运行之前在屏幕上渲染某些内容,从而提高用户感知的性能指标。组件的性能不等于它运行的渲染次数。
  • @StephenKingsley 虽然有这个计划,但他们并没有这样做,因为如果您忘记将字段添加到监视字段,或者如果您正在使用它会导致很多混乱行为具有私有状态的复合挂钩调用。

标签: javascript reactjs react-hooks rerender


【解决方案1】:

这是 StrictMode 的一个有意的功能。这只发生在 开发,并有助于发现意外的副作用放入 渲染阶段。我们只对带有 Hooks 的组件执行此操作,因为那些 更容易在错误的地方意外产生副作用。 -- gaearon commented on Mar 9, 2019

【讨论】:

  • 我在这里疯了,答案是“这是一个功能,而不是一个错误”。无论哪种方式,+1,谢谢!
【解决方案2】:

如果我们只是想做类似componentShouldUpdate的事情,我们可以使用useMemo。

function DC() {
  const [count, setCount] = useState(0);
  const [sum, setSum] = useState(0);
  const memoizedJOKER = useMemo(() => <JOKER count={count} />, [count]);
  return (
    <div>
      <button onClick={() => {
        // setCount(count + 1);
        setSum(sum + 1);
        console.log('---click---');
        console.log('\n');
      }}>
        Click me
      </button>
      <p>DC: You clicked {count} times</p>
      <p>now this is {sum} times</p>
      {memoizedJOKER}
    </div>
  );
}

当你点击按钮时,JOKER 不会再次渲染。

【讨论】:

    【解决方案3】:

    我不确定我是否理解你的问题,但这里是。

    当您的&lt;DC /&gt; 组件更改状态时,它会将新的状态值count 传递给组件Joker。此时组件将重新渲染,考虑到第一次更改。

    然后你将效果绑定到props.count更改;

      useEffect(() => {
        console.log('I am JOKER\'s useEffect--->', props.count);
        setCount(props.count); 
      }, [props.count]);// <-- This one
    

    当组件从组件 DC 获取新值时触发。它会将自身Joker 的状态设置为props.count,这会导致组件重新渲染。

    然后为您提供以下输出:

    I am JOKER's  render--> 1 // Initial render where Joker receives props from DC
    index.js:27 I am JOKER's useEffect---> 2 // The hook runs because props.count changed
    index.js:27 I am JOKER's  render--> 2 // Joker rerenders because its state updated.
    

    【讨论】:

    • 哇,这就是我的意思!如果 DC 的状态发生变化,JOKER 将启动渲染。你认为这是不必要的渲染吗?
    • @StephenKingsley React 中组件的生命周期是以一种初始渲染的方式制作的。如果您考虑一下,如果在挂载时至少没有运行一次,它将如何运行任何代码?如果您了解 C# 之类的知识,请考虑最初运行的类中的构造函数。查看功能组件基本上是内联逻辑和特定于特定逻辑单元的视图,这是有意义的。 “弄清楚该做什么,然后展示它”。你可以在这里阅读更多关于生命周期的信息reactjs.org/docs/state-and-lifecycle.html
    • @Dannis 这与组件生命周期无关。 Hooks 不是类组件,它没有任何生命周期。我关心的是单击按钮后,joker 会先渲染,然后使用 useEffect,然后再渲染。我知道 Hooks 的顺序,只是我不想第一次渲染。
    • 我很清楚这一点,但让我换个方式问你。如果您不运行代码所在的功能范围,您将如何运行代码 useEffect?这不可能。它本身不是“渲染”,它只是一个函数。您不能同时运行和不运行函数体。这不是函数的工作方式。如果你不想要实际的渲染(DOM 部分),那么你可以用一个变量来控制它,但你不能阻止代码实际运行。
    • 如果我们使用类组件,有shouldComponentUpdate和pureComponent。但是现在我们在 Hooks 中有什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 2019-05-26
    • 1970-01-01
    相关资源
    最近更新 更多