上节课我们介绍了state hook,这节课我们来介绍一下effect hook

回顾

在开始学习effect hook之前,我们来对上节课的内容来做一个简单的回顾:

  • hooks可以让函数式组件能够使用reactstate已经其他的特性
  • useState方法会返回一个state和一个更新state的方法
  • 更新state的方法和setState的用法完全一致

以上便是上节课的内容,接下来我们来介绍一下effect hook

effect hook

这个hook是做什么的呢?我们知道我们有生命周期钩子,但是哪些是不是在类组件才能用的啊?而effect hook的作用就是让函数式组件也可以使用生命周期钩子,那么我们来想一个需求,还是刚才哪个案例,我们现在不点了,我们要求每隔 1 秒,数字自增长加一,那么怎么写?

在类组件中我们是不是componentDidMount中设置循环计时器啊?那么函数式组件又怎么办呢?

import React from 'react';

export default function Demo() {
  const [ state, setState ] = React.useState({ sum: 0 });
  React.useEffect(()=>{
    console.log("add")
  })
  function add() {
    setState(state => ({ sum: state.sum + 1 }))
  }
  return (
    <div>
      <h2>sum is : {state.sum}</h2>
      <button onClick={add}>click</button>
    </div>
  );
}

我们来看代码,我们说了effect hook可以让函数式组件也使用生命周期钩子,那么我们来调用这个hook吧,这个useEffect就是调用effect hook的方法,但是 这个方法得传入一个函数,因为我们生命周期钩子是函数啊,而且生命周期钩子也不知一个,所以我们得传入一个函数至于怎么区分是哪个钩子,我们先来看现在这个效果我们应该算是哪个钩子呢?

image-20220120155430406

我们刚打开页面,控制台就自动输出了add,那么是不是代表我们的钩子被调用了?那么什么钩子是在组件一完成挂载马上就开始调用的呢?是不是componentDidMount钩子?但是呢:

image-20220120155458571

我点击按钮,这个add还一直在输出,那么我们现在还能说这个钩子是componentDidMount钩子吗?肯定不是啊,这个钩子感觉是不是相当于componentDidUpdate啊?那么我们能把定时器写在这里吗?是不是根本不能?那么我们怎么办呢?我们来说一下,useEffect方法其实可以传两个参数,第二个参数代表着我们的effect hook监测对象,一旦监测对象发生更新,马上就调用我们传进去的函数。我们默认不传的时候就是监测所有东西,那么我们如果传一个空数组的话会怎么样呢?

image-20220120160411252

我们现在点击了 14 次按钮,但是只输出了一次add,那么这次是为什么呢?因为我们传了个空数组,那么不就代表啥也没监测嘛,所以说在初始化的时候就执行一次,那么这下行了,我们这次就相当于componentDidMount了,开个定时器呗:

import React from 'react';

export default function Demo() {
  const [ state, setState ] = React.useState({ sum: 0 });
  React.useEffect(()=>{
    setInterval(()=>{setState(state=>({sum: state.sum+1}))},1000)
  },[])
  function add() {
    setState(state => ({ sum: state.sum + 1 }))
  }
  return (
    <div>
      <h2>sum is : {state.sum}</h2>
      <button onClick={add}>click</button>
    </div>
  );
}

这次我们来看代码,首先我们让effect hook谁也不监测,那么久在挂载的时候执行一次,然后开启定时器,然后修改状态,那么我们来看一下效果是什么样子:

iShot2022-01-20 16.13.24

我们这个录得gif动作有点慢,但是效果还是正常实现了的。

import React from 'react';
import ReactDOM from 'react-dom';

export default function Demo() {
  const [ state, setState ] = React.useState({ sum: 0 });
  React.useEffect(()=>{
    setInterval(()=>{setState(state=>({sum: state.sum+1}))},1000)
  },[])
  function add() {
    setState(state => ({ sum: state.sum + 1 }))
  }
  function unmount(){
    ReactDOM.unmountComponentAtNode(document.getElementById("root"));
  }
  return (
    <div>
      <h2>sum is : {state.sum}</h2>
      <button onClick={add}>click</button>
      <button onClick={unmount}>unmount</button>
    </div>
  );
}

我们再来看这一段,我们现在这个组件不要了,我们要卸载,我们写了一个按钮,只要一点击就会卸载,那么我们来看一下效果:

image-20220120161858052

我们来看一下,组件确实已经卸载了,但是计时器没关,那么我们是不是要有一个钩子来在卸载前把计时器关掉啊?那么是不是就要用到componentWillUnmount钩子了?那么hook中我们应该监测谁来确定组件要卸载了呢?

那么我可以直接告诉大家,这一点很重要,我们的useEffect方法接收的第一个参数是一个函数,这个函数需要返回一个函数,而这个返回出来的函数就componentWillUnmount,我们来看一下:

import React from 'react';
import ReactDOM from 'react-dom';

export default function Demo() {
  const [ state, setState ] = React.useState({ sum: 0 });
  React.useEffect(()=>{
    setInterval(()=>{setState(state=>({sum: state.sum+1}))},1000)
    return ()=>{console.log("will unmount")}
  },[])
  function add() {
    setState(state => ({ sum: state.sum + 1 }))
  }
  function unmount(){
    ReactDOM.unmountComponentAtNode(document.getElementById("root"));
  }
  return (
    <div>
      <h2>sum is : {state.sum}</h2>
      <button onClick={add}>click</button>
      <button onClick={unmount}>unmount</button>
    </div>
  );
}

大家看到了没有?我们没有新写一个useEffect,而是在原来的useEffect中的第一个参数里面返回了一个函数,也就是说我们有一个useEffect方法同时解决掉了两个生命周期钩子,一个是componentDidMount另一个是componentWillUnmount,那么我们来看一下有没有调用这个函数:

image-20220120162656947

组件卸载了,函数也调用了,那么我们接下来就在这个函数里面来关闭我们的计时器看报错能不能消失掉:

import React from 'react';
import ReactDOM from 'react-dom';

export default function Demo() {
  const [ state, setState ] = React.useState({ sum: 0 });
  React.useEffect(()=>{
    let timer = setInterval(()=>{setState(state=>({sum: state.sum+1}))},1000)
    return ()=>{clearInterval(timer);}
  },[])
  function add() {
    setState(state => ({ sum: state.sum + 1 }))
  }
  function unmount(){
    ReactDOM.unmountComponentAtNode(document.getElementById("root"));
  }
  return (
    <div>
      <h2>sum is : {state.sum}</h2>
      <button onClick={add}>click</button>
      <button onClick={unmount}>unmount</button>
    </div>
  );
}

我们来看,关闭计时器要给一个id把?我们之前是把计时器挂在类组件实例自身的,现在函数没有this啊,那么我们定义一个变量不就行了嘛,我们来看一下效果:

image-20220120163001838

卸载成功了,而且也没有报错

总结

  • effect hook是为了使函数式组件使用生命周期钩子
  • useEffect第二个参数传空数组来标识谁也不监测
  • useEffect第一个参数是个函数,该函数返回的函数相当于componentWillUnmount

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/extend_effect_hook/