自定义钩子内的useEffect不会在React中的正确oreder中被调用

用户名

我在useEffect称为的自定义钩子内useCustomHook使用,useCustomHook在两个组件(即(First,Second))中使用了它,但是useEffect仅在FirstSecond组件被渲染时才被调用

例如

我有一个第一部分

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

我的控制台输出是:

1)组件先渲染

2)useCustomHook被调用

3)组件二次渲染

4)useCustomHook被调用

5)(2)调用useEffect

我想知道

为什么行5输出不在行之后2,为什么Second组件日志在line之后发生2,因为useEffect应该useCustomHookFirst组件被调用之后但在该Second组件日志被调用之前调用。为什么useEffect内部useCustomHook没有在Second组件日志之前被调用

优素福

您的输出是应该的。

我认为您对输出感到困惑,因为您认为这与输出useEffect相同componentDidMount但不正确。它们两者都是不同的,它们之间的几个重要区别如下:

他们在不同的时间运行

(与您的问题有关)

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

捕获道具和状态

(与您的问题无关,请随时跳到答案的结尾)

useEffect捕获状态和道具,componentDidMount但不这样做。

考虑以下代码片段,以了解useEffect捕获状态和道具的含义。

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>

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

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

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

如果您认为componentDidMountuseEffect相同,那么你可能会惊讶地看到,无论是代码片段记录的不同值count4秒钟后变量。

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

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

  • this.state内部类组件始终指向最新状态,因此它将记录count4秒后的最新值

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

要深入了解useEffect之间的区别componentDidMount,建议您阅读以下文章

现在回到您的问题

如果您关注我的回答是关系到你的问题的第一部分,你可能现在明白为什么useEffect两个后运行其回调FirstSecond组件已经安装。

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

在执行useCustomHookFirst组件内部调用函数之后First将挂载组件,如果它是基于类的组件,则此时componentDidMount将调用生命周期函数。

First安装Second组件之后,再进行组件安装,如果这也是基于类的组件,则此时componentDidMount将调用生命周期功能。

在安装了两个组件之后,浏览器会绘制屏幕,​​结果,您会在屏幕上看到输出。浏览器绘制完屏幕后,useEffect的回调函数将同时针对FirstSecond组件执行

简而言之,useEffect让浏览器在运行效果/回调之前先绘制屏幕。这就是useEffect gets called在输出末尾记录日志的原因

您可以在官方文档中查看有关此内容的更多详细信息:效果时序

如果您打开FirstSecond在对类组件的组件,然后输出应该是:

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每个组件的执行。

如果创建ThirdFourth组件并创建以下类组件层次结构:

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

那么您将获得以下输出:

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Reactjs 自定义钩子不会在 useEffect 中使用 if/else 触发?

React - nextjs 在 useEffect 中调用自定义钩子

反应| UseCallBack中的道具不会在自定义钩子中更新

自定义错误功能不会在php中调用

RowDeselect不会在自定义MvxTableViewSource中触发

颜色不会在自定义类别中显示

自定义挂钩中的useEffect不会在删除时重新呈现

setInterval不会在fadeOut中被调用

React 中未调用自定义钩子函数

自定义过渡动画不会在关闭时调用VC生命周期方法

调用多个 NPM 脚本的自定义 NPM 脚本不会在失败时停止

即使使用 useEffect 钩子的返回回调,也不会在 React 中删除事件侦听器

自定义DependencyProperty不会在动画上更新

示例中的自定义自动完成不会在对话框中打开

在钩子内反应调用自定义钩子

引用问题和useEffect后调用自定义钩子

Cocos2d-js:自定义TTF字体不会在Android中显示

Aurelia-更改集合后不会在repeat.for中创建自定义元素

具有自定义颜色属性的 TextView 不会在布局预览中呈现

自定义客户端验证不会在ASP.NET Core中触发

自定义Angular组件不会在模板中呈现为TR

子项的自定义事件后,插值不会在父组件中重新呈现

自定义插件不会在Minecraft中显示吗?

自定义的错误消息不会在ASP.NET MVC 4中转换

Matplotlib不会在自定义图例中显示剖面线

Azure不会在HttpException中传递我的自定义消息

cmake不会在自定义命令中复制文件

聚合物中不会在页面上显示计算的自定义属性

UIWebView 委托不会在自定义类中触发