React Hooks 渲染两次

IT技术 javascript reactjs react-hooks rerender
2021-04-02 20:56:52

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

下面有两个组件 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"))
3个回答

这是 StrictMode 的一个有意特性。这仅发生在开发中,并有助于发现进入渲染阶段的意外副作用。我们只对带有 Hook 的组件执行此操作,因为它们更有可能在错误的地方意外产生副作用。-- gaearon 于 2019 年 3 月 9 日发表评论

我在这里失去理智,答案是“这是一个功能,而不是一个错误”。无论哪种方式,+1,谢谢!
2021-06-02 20:56:52

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

当您的<DC />组件更改状态时,它会将新的状态值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.
@StephenKingsley React 中组件的生命周期是以它进行初始渲染的方式进行的。如果您考虑一下,如果挂载时它至少不运行一次,它将如何运行任何代码?如果您了解 C# 之类的东西,请考虑最初运行的类中的构造函数。查看功能组件基本上是内联逻辑和特定于某个有意义的逻辑单元的视图。“弄清楚要做什么,然后展示它”。你可以在reactjs.org/docs/state-and-lifecycle.html 中阅读更多关于生命周期的内容
2021-05-22 20:56:52
我很清楚这一点,但让我换个方式问你。如果不运行代码 useEffect 所在的功能范围,您将如何运行它?这不可能。它本身不是“渲染”,它只是一个函数。您不能同时运行和不运行函数体。这不是函数的工作方式。如果你不想要实际的渲染(DOM 部分),那么你可以用一个变量来控制它,但你不能阻止代码实际运行。
2021-05-22 20:56:52
@Dannis 这与组件生命周期无关。Hooks 不是类组件,它没有任何生命周期。我关心的是点击按钮后,小丑先渲染,然后使用效果,然后再渲染。我知道 Hooks 的顺序,只是我不想要第一个渲染。
2021-05-27 20:56:52
哇,这就是我的观点!如果 DC 的状态发生变化,JOKER 将启动渲染。您认为这是不必要的渲染吗?
2021-06-06 20:56:52
如果我们使用类组件,则有 shouldComponentUpdate 和 pureComponent。但是现在我们在 Hooks 中有什么?
2021-06-10 20:56:52

如果我们只想做一些类似 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 不会再次渲染。