useEffect 在第一次重新渲染时运行两次,然后在第二次重新渲染时运行一次

问题描述 投票:0回答:1

React 在第一次渲染时以严格模式挂载、卸载和重新挂载组件。此后,对于每个渲染,严格模式都会渲染组件两次,但第二次渲染是第一次渲染的副本,因此 useEffect 也应该运行一次。

代码:

import React from "react";
import "./style.css";

import {useRef, useState, useEffect} from 'react';

export default function App(){
    const [flag, setFlag]=useState(0);
    const ref = useRef(0);
    const ref2 = useRef(0);
    useEffect(()=>{
      ref2.current+=1;
    }
    )
    ref.current+=1;
    return(
        <>
            <button onClick={()=>setFlag(flag+1)}>Click</button>
            <p>Number of renders without Effect : {ref.current}</p>
            <p>Number of renders with Effect : {ref2.current}</p>
        </>
    )
}

因此在严格模式下的初始渲染期间,

1.元件贴装:

在 JSX 加载之前:

ref.current holds 1; 
ref2.current holds 0

JSX 加载后:

UI shows ref.current as 1 and ref2.current as 0; 
useEffect runs => ref2.current becomes 1

2.组件卸载:

组件被删除。

3.组件重新安装:

在 JSX 加载之前:

ref.current holds 1; 
ref2.current holds 0

JSX 加载后:

UI shows ref.current as 1 and ref2.current as 0; 
useEffect runs => ref2.current becomes 1

所以严格模式下的初始渲染输出如下:

Click
Number of renders without Effect : 1

Number of renders with Effect : 0

现在,如果我们按“Click”,在严格模式下,根据我的理解,尽管渲染发生两次,但 useEffect 执行一次。

但是现在,UI 更改为将 ref.current 显示为 3,将 ref2.current 显示为 2,这意味着增加了 2。这意味着 useEffect 运行了两次。

现在对于后续的点击,useEffect 只运行一次,并且每次都会加 1。 如何解释这种行为?

编辑:我已将 ref2.current 本身记录在 useEffect 中,并在初始渲染后打印 1 和 2。它打印 2 的原因是为了重新安装并调用 useEffect,因此在渲染时最初 ref2.current 会是 1?但为什么在 UI 中它打印为 0?

reactjs react-hooks
1个回答
0
投票

初始渲染:

在严格模式下,React 会调用你的组件函数两次,以帮助你发现意外的杂质。这只是开发行为,不会影响生产。 每个 ref 对象都会被创建两次,但其中一个版本将被丢弃。

这就是为什么

ref.current
在 UI 中是
1
ref2.current
0

传入的效果函数

useEffect()
会在调用组件函数后执行。

组件安装时效果运行两次

当严格模式开启时,在开发过程中,React 会在实际设置之前额外运行一次设置和清理操作。

因此

ref2.current += 1
将在效果函数中执行两次。该值变为
2
,但此时 UI 中显示的
ref2.current
0
,因为我们没有更新状态并触发组件的下一次渲染。

当点击按钮时,更新标志状态,触发组件的下一次渲染。

ref2.current
将在 UI 上显示
2

严格模式下:

你的组件将重新渲染额外的时间来查找不纯渲染引起的错误。

ref.current += 1
会执行两次,所以UI中的
ref.current: 3

useEffect()
中的效果函数会在组件更新后执行一次,所以效果函数中的
ref2.current
变为
3

再次点击按钮,触发下一次渲染。

ref.current
将显示
5
ref2.current
将显示
3

然后,

7
4
9
5
11
6
,继续这样下去

查看此示例的控制台日志

© www.soinside.com 2019 - 2024. All rights reserved.