javascript - 自定义钩子中的useEffect没有在React中以正确的顺序调用
问题描述
我useEffect
在一个名为的自定义钩子useCustomHook
中使用,我useCustomHook
在两个组件中使用它,即(第一,第二),但只有在和组件得到渲染useEffect
时才被调用。First
Second
例如
我有一个第一个组件
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])
}
我的主要应用程序组件
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
组件日志在行之后发生2
,因为useEffect
应该在组件调用之后但在useCustomHook
调用组件日志First
之前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 次) 。
如果您认为componentDidMount
并且useEffect
相同,那么您可能会惊讶地发现两个代码片段count
在 4 秒后记录了不同的变量值。
基于类的代码片段记录最新值,而基于功能组件的代码片段记录count
变量的初始值。
他们记录不同count
变量值的原因是:
this.state
内部类组件始终指向最新状态,因此它会记录count
4 秒后的最新值。useEffect
捕获变量的初始值count
并记录捕获的值而不是最新值。
要深入解释 和 之间的区别useEffect
,componentDidMount
我建议您阅读以下文章
回到你的问题
如果您注意了与您的问题相关的我的答案的第一部分,您现在可能明白为什么在两个组件都安装后useEffect
运行它的回调。First
Second
如果不是,那么让我解释一下。
在执行useCustomHook
从组件内调用的函数后First
,First
组件被挂载,如果它是基于类的组件,componentDidMount
则此时将调用其生命周期函数。
组件挂载后First
,Second
组件挂载,如果这也是基于类的组件,则此时componentDidMount
将调用其生命周期函数。
安装两个组件后,浏览器会绘制屏幕,结果,您会在屏幕上看到输出。在浏览器绘制完屏幕后,useEffect 的回调函数会同时为First
组件执行Second
。
简而言之,useEffect
让浏览器在运行其效果/回调之前绘制屏幕。这就是为什么useEffect gets called
在输出末尾记录的原因。
您可以在官方文档中查看更多详细信息:效果时间
如果您将组件转入First
类Second
组件,则输出将如下:
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
每个组件的执行。
如果您创建Third
和Fourth
组件并创建以下类组件的层次结构:
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
推荐阅读
- reactjs - Cannot set useState hook value to opposite boolean
- generics - 通过反射的 Kotlin 数据类实例化
- selenium - 无法编写步骤定义出现错误?
- email - 是否可以在不更改所有电子邮件提供商后端的任何代码的情况下为“at 符号”添加另一个 Unicode 字符?
- python - 在轴上显示带有坐标的字母网格
- android - 如何解决运行时错误访问拒绝查找属性“vendor.debug.egl.profiler”?
- c# - 从参数类型派生方法返回类型
- javascript - 如何等待连接。连接
- css - 在 slim 框架项目中包含 css 文件
- visual-studio - 为本地 Azure Service Fabric 生成 Intellitrace 日志文件