为了账号安全,请及时绑定邮箱和手机立即绑定

使用在 React 中未正确调用自定义钩子内部的效果

使用在 React 中未正确调用自定义钩子内部的效果

慕姐8265434 2022-09-29 15:28:16
我正在使用一个名为的自定义钩子,我在两个组件(第一,第二)中使用它,但仅在和组件得到渲染时才被调用。useEffectuseCustomHookuseCustomHookuseEffectFirstSecond例如我有第一个组件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>)}这是我的自定义钩子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)使用被调用的自定义钩子3) 组件 二次渲染4)使用被调用的自定义钩子5) (2) 使用效果被调用我想知道为什么是行输出不是在行之后,为什么组件日志发生在行之后,因为应该在被组件调用之后但在调用组件日志之前调用。为什么在组件日志之前没有被调用。52Second2useEffectuseCustomHookFirstSeconduseEffectuseCustomHookSecond
查看完整描述

2 回答

?
暮色呼如

TA贡献1853条经验 获得超9个赞

您的输出是应该的。

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

它们在不同的时间运行

(与您的问题相关)

它们都是在组件的初始呈现之后调用的,但在浏览器绘制屏幕之后调用,而在浏览器绘制屏幕之前调用。useEffectcomponentDidMount

捕捉道具和状态

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

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

请考虑以下代码片段,以了解使用效果捕获状态和道具的含义。

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>

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

这两个代码段都有一个名为 状态的变量,它们都在 4 秒后将变量的值记录到控制台。它们还包括一个按钮,可用于递增 .countcountcount

尝试单击按钮(4 或 5 次),然后将 的值记录在控制台上。count

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

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

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

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

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

有关 和 之间的差异的深入说明,我建议您阅读以下文章useEffectcomponentDidMount


回到你的问题

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

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

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

在组件挂载后,组件挂载,如果这也是一个基于类的组件,则此时将调用其生命周期函数。FirstSecondcomponentDidMount

安装完两个组件后,浏览器将绘制屏幕,因此,您会在屏幕上看到输出。浏览器绘制完屏幕后,将对和组件执行 useEffect 的回调函数。FirstSecond

简而言之,让浏览器在运行其效果/回调之前绘制屏幕。这就是为什么记录在输出的末尾。useEffectuseEffect 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


查看完整回答
反对 回复 2022-09-29
?
倚天杖

TA贡献1828条经验 获得超3个赞

你提到的顺序完全有道理,这就是钩子的工作原理。

流:

  • First组件开始执行。

  • 在组件中,在代码行之后,将执行FirstuseCustomHook(count)useCustomHook

  • 在控制台中.log,并执行 useEffect,并且使用效果采取的回调为“已注册”和“未执行”。useCustomHook

  • First组件返回 JSX。即组件被安装/渲染。

  • 一旦组件被挂载,就会调用使用效果的回调。FirstuseCustomHook

  • 基本上,组件内部的范围限定为组件。useCustomHookFirst

第二组件也是如此...


查看完整回答
反对 回复 2022-09-29
  • 2 回答
  • 0 关注
  • 82 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信