繁体   English   中英

自定义挂钩内的 useEffect 未在 React 中以正确的顺序调用

[英]useEffect inside custom hook not getting called in correct oreder in React

我在一个名为useCustomHook useEffect我在两个组件(即(第一,第二))中使用这个useCustomHook ,但是只有当FirstSecond组件被渲染时才会调用useEffect

例如

我有一个第一个组件

import React,{useState} from 'react'
import useCustomHook from './customHook'

function First(){
 console.log("component First rendering")
 const [count,setCount]=useState(0)
 useCustomHook(count)

 return (<div>First component</div>)

}

这是我的第二个组成部分

import React,{useState} from 'react'
import useCustomHook from './customHook'

function Second(){
 console.log("component Second rendering")
 const [count,setCount]=useState(0)
 useCustomHook(count)

 return (<div>Second component</div>)

}

这是我的 customHook

import {useEffect} from 'react'

function useCustomHook(count){
  console.log("useCustomHook getting called")
  useEffect(()=>{
 console.log("useEffect gets called") //this function is running after both component rendered
  },[count])

}

我的主要 App 组件

import First from './first'
import Second from './second'

function App(){
   return (
      <div>
        <First/>
        <Second/>
      </div>
    )
}

我的控制台 output 是:

1)组件第一次渲染

2) useCustomHook 被调用

3)组件二次渲染

4) useCustomHook 被调用

5) (2) useEffect 被调用

我想知道

为什么第5行 output 不在第2行之后,为什么Second component log 发生在第2行之后,因为useEffect应该在First组件调用useCustomHook之后但在调用Second component log 之前调用。 为什么在Second组件日志之前没有调用useEffect中的useCustomHook

你的 output 是应该的。

我认为您对 output 感到困惑,因为您认为useEffectcomponentDidMount相同,但这是不正确的。 它们都是不同的,下面提到了它们之间的几个重要区别:

他们在不同的时间运行

(与你的问题有关)

它们都在组件的初始渲染之后调用,但是useEffect在浏览器绘制屏幕之后调用,而componentDidMount在浏览器绘制屏幕之前调用。

捕获道具和 state

(与你的问题无关,请跳至答案末尾)

useEffect捕获 state 和 props 而componentDidMount不这样做。

考虑以下代码片段以了解useEffect 捕获 state 和 props 的含义。

 class App extends React.Component { constructor() { super(); this.state = { count: 0 }; } componentDidMount() { setTimeout(() => { console.log('count value = ' + this.state.count); }, 4000); } render() { return ( <div> <p>You clicked the button { this.state.count } times</p> <button onClick={ () => this.setState(prev => ({ count: prev.count + 1 })) }> Increment Counter </button> </div> ); } } ReactDOM.render(<App />, document.getElementById('root'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="root"></div>

 function App() { const [count, setCount] = React.useState(0); React.useEffect(() => { setTimeout(() => { console.log('count value = ' + count); }, 4000); }, []) return ( <div> <p>You clicked the button { count } times</p> <button onClick={ () => setCount(count + 1) }> Increment Counter </button> </div> ); } ReactDOM.render(<App />, document.getElementById('root'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <div id="root"></div>

这两个代码片段是相同的,除了第一个具有基于 class 的组件而第二个具有功能组件。

这两个片段在 state 中都有一个名为count的变量,它们都在 4 秒后将count变量的值记录到控制台。 它们还包括一个按钮,可用于增加count的值。

在控制台记录count之前尝试单击按钮(4 或 5 次)。

如果您认为componentDidMountuseEffect相同,那么您可能会惊讶地发现这两个代码片段在 4 秒后记录了不同的count变量值。

基于 Class 的代码片段记录最新值,而基于功能组件的代码片段记录count变量的初始值。

他们记录不同count变量值的原因是:

  • this.state组件中的 this.state 始终指向最新的 state,因此它会在 4 秒后记录最新的count

  • useEffect捕获count变量的初始值并记录捕获的值而不是最新值。

为了深入解释useEffectcomponentDidMount之间的区别,我建议你阅读以下文章


回到你的问题

如果您注意了我回答中与您的问题相关的第一部分,您现在可能明白为什么useEffectFirstSecond组件都挂载后运行其回调。

如果没有,那么让我解释一下。

在执行从第First组件中调用的useCustomHook function 之后, First组件被挂载,如果它是一个基于 class 的组件,它的componentDidMount生命周期 function 将在此时被调用。

First组件挂载后, Second组件挂载,如果这也是一个基于 class 的组件,则此时将调用其componentDidMount生命周期 function。

两个组件都安装后,浏览器绘制屏幕,结果,您会在屏幕上看到 output。 浏览器绘制屏幕后,会为FirstSecond组件执行 useEffect 的回调 function。

简而言之, useEffect让浏览器在运行其效果/回调之前绘制屏幕。 这就是为什么useEffect gets called记录在 output 的末尾。

你可以在官方文档上看到更多细节:Timing of effects

如果将FirstSecond组件转换为 class 组件,则 output 将是:

1. component First rendering
2. component Second rendering
3. component First mounted.      // console.log statement inside componentDidMount
4. component Second mounted.     // console.log statement inside componentDidMount

您可能希望第 3 行位于第 2 位,第 2 行位于第 3 位,但事实并非如此,因为 React 在将所有子组件插入 DOM 之前以及仅在它们插入 DOM 之后首先执行渲染函数,每个componentDidMount的 componentDidMount 执行。

如果您创建ThirdFourth组件并创建以下 class 组件的层次结构:

App
 |__ First
 |     |__ Third
 |          |__ Fourth
 | 
 |__ Second

那么您将看到以下 output:

1.  First component constructor
2.  component First rendering
3.  Third component constructor
4.  component Third rendering
5.  Fourth component constructor
6.  component Fourth rendering
7.  Second component constructor
8.  component Second rendering
9.  component Fourth mounted
10. component Third mounted
11. component First mounted
12. component Second mounted

您提到的顺序非常合理,这就是挂钩的工作原理。

流动:

  • First组件开始执行。
  • First组件中,在useCustomHook(count)行代码之后,将执行useCustomHook
  • useCustomHook中,打印了 console.log 并执行了 useEffect,并且注册了使用效果所采用的回调,但未执行。
  • First组件返回 JSX。 即组件已安装/呈现。
  • 一旦First组件被挂载,就会调用useCustomHook中的 useEffect 回调。
  • 基本上, First组件内的useCustomHook的范围仅限于该组件。

第二个组件也一样......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM