![](/img/trans.png)
[英]React: addEventListener() returns error when called inside of useEffect hook
[英]useEffect inside custom hook not getting called in correct oreder in React
我在一个名为useCustomHook
useEffect
我在两个组件(即(第一,第二))中使用这个useCustomHook
,但是只有当First
和Second
组件被渲染时才会调用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 感到困惑,因为您认为useEffect
与componentDidMount
相同,但这是不正确的。 它们都是不同的,下面提到了它们之间的几个重要区别:
(与你的问题有关)
它们都在组件的初始渲染之后调用,但是useEffect
在浏览器绘制屏幕之后调用,而componentDidMount
在浏览器绘制屏幕之前调用。
(与你的问题无关,请跳至答案末尾)
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 次)。
如果您认为componentDidMount
和useEffect
相同,那么您可能会惊讶地发现这两个代码片段在 4 秒后记录了不同的count
变量值。
基于 Class 的代码片段记录最新值,而基于功能组件的代码片段记录count
变量的初始值。
他们记录不同count
变量值的原因是:
this.state
组件中的 this.state 始终指向最新的 state,因此它会在 4 秒后记录最新的count
。
useEffect
捕获count
变量的初始值并记录捕获的值而不是最新值。
为了深入解释useEffect
和componentDidMount
之间的区别,我建议你阅读以下文章
回到你的问题
如果您注意了我回答中与您的问题相关的第一部分,您现在可能明白为什么useEffect
在First
和Second
组件都挂载后运行其回调。
如果没有,那么让我解释一下。
在执行从第First
组件中调用的useCustomHook
function 之后, First
组件被挂载,如果它是一个基于 class 的组件,它的componentDidMount
生命周期 function 将在此时被调用。
First
组件挂载后, Second
组件挂载,如果这也是一个基于 class 的组件,则此时将调用其componentDidMount
生命周期 function。
两个组件都安装后,浏览器绘制屏幕,结果,您会在屏幕上看到 output。 浏览器绘制屏幕后,会为First
和Second
组件执行 useEffect 的回调 function。
简而言之, useEffect
让浏览器在运行其效果/回调之前绘制屏幕。 这就是为什么useEffect gets called
记录在 output 的末尾。
你可以在官方文档上看到更多细节:Timing of effects
如果将First
和Second
组件转换为 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 执行。
如果您创建Third
和Fourth
组件并创建以下 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.