ReactHooks专题-useEffect

链接: 文档地址.

Effect Hook

目前我们已经通过hook函数式组件中定义state,那么类似于生命周期这些呢?

  • Effect Hook可以让你来完成一些类似于class中生命周期的功能;
  • 事实上,类似于网络请求,手动更新DOM,一些事件的监听,都是React更新DOM的一些副作用;
  • 所以对于完成这些功能的Hook被称之为Effect Hook

Effect的基本使用

假如我们现在有一个需求:页面的title总是显示counter数字
使用class组件如何实现呢?

  • 我们会发现document.title的设置必须在两个生命周期中完成;
  • 这是因为Reactclass的组件并没有给我们提供一个统一的生命周期函数,可以让无论是否是第一次渲染都会执行生命周期的函数;
import React, { Component } from 'react';

class ClassCom extends Component {
  state={
    counter:96
  }
  componentDidMount(){
    document.title=this.state.counter
  }
  componentDidUpdate(){
    document.title=this.state.counter
  }
  render() {
    return (
      <div>
        <p>当前counter的是{this.state.counter}</p>
        <button onClick={()=>{
          this.setState({
            counter:this.state.counter+1
          })
        }}>+</button>
        <button onClick={()=>{
          this.setState({
            counter:this.state.counter-1
          })
        }}>-</button>
      </div>
    );
  }
}

export default ClassCom;

这个时候,我们可以使用useEffectHook来完成;

import React, { useEffect, useState } from 'react';

export default function About() {
  let [counter,setCounter]=useState(66)

  useEffect(()=>{
    document.title=counter;
  })
  
  return (
    <div>
      <p>当前counter的计数是{counter}</p>
      <button onClick={()=>{
        setCounter(counter+1)
      }}>+</button>
      <button onClick={()=>{
        setCounter(counter-1)
      }}>-</button>
    </div>
  );
}

useEffect的解析;

  • 通过useEffectHook,可以告诉React需要在渲染后执行某些操作;
  • useEffect要求我们传入一个回调函数,在React执行完更新DOM操作之后,就会回调这个函数
  • 默认情况下,无论是第一次渲染之后,还是每次更新之后,都会执行这个回调函数

需要清除useEffect

class组件在编写的过程中,某些副作用代码,我们需要在componentWillUnmount中进行清除;

  • 比如我们之前的事件总线或Redux中手动调用subscribe
  • 都需要在componentWillUnmount有对应的取消订阅
  • Effect Hook通过什么方式来模拟componentWillUnmount

useEffect传入的回调函数A本身可以有一个返回值,这个返回值是另外一个回调函数B

type EffectCallback = () => (void | (() => void | undefined));

我们可以这样来编写Effect Hook

import React, { useEffect, useState } from 'react';

export default function About() {
  let [counter,setCounter]=useState(66)

  useEffect(()=>{
    document.title=counter;
    console.log('每次DOM更新时会回调')
    return ()=>{
      console.log('DOM被移除时会回调');
    }
  })
  
  return (
    <div>
      <p>当前counter的计数是{counter}</p>
      <button onClick={()=>{
        setCounter(counter+1)
      }}>+</button>
      <button onClick={()=>{
        setCounter(counter-1)
      }}>-</button>
    </div>
  );
}

为什么要在effect中返回一个函数

  • 这是Effect可选的清除机制。每个effect都可以返回一个清除函数;
  • 如此可以将添加和移除订阅的逻辑放在一起;
  • 它们都属于effect的一部分;

React何时清除effect

  • React会在组件更新和卸载的时候执行清除操作;
  • 正如之前学到的,Effect在每次渲染的时候都会执行;

使用多个Effect

使用Hook的其中一个目前就是解决class中生命周期经常将很多的逻辑放在一些的问题

  • 比如网络请求,事件监听,手动修改DOM,这些往往都会在componentDidMount中;

使用Effect Hook,我们可以将它们分离到不同的useEffect中;

import React, { useEffect } from 'react';

export default function MultiUseEffect() {
  useEffect(() => {
    console.log("网络请求");
  });

  useEffect(() => {
    console.log("修改DOM");
  })

  useEffect(() => {
    console.log("事件监听");

    return () => {
      console.log("取消监听");
    }
  })

  return (
    <div>
      <h2>MultiUseEffect</h2>
    </div>
  )
}

Hook允许我们按照代码的用途分离它们,而不是像生命周期函数那样;

  • React将按照effect生命的顺序依次调用组件中每一个effect

Effect性能优化

默认情况下,useEffect的回调函数都会在每次渲染的时候都重新执行,但是这会导致两个问题;

  • 某些代码我们只希望执行一次即可,类似于componentDidMountcomponentWillUnmount中完成的事情;(比如网络请求,订阅和取消订阅)
  • 另外,多次执行也会导致一定的性能问题;

我们如何决定useEffect在什么时候应该执行和什么时候不应该执行呢?

  • useEffect实际上有两个参数;
  • 参数一:执行的回调函数
  • 参数二:该useEffect在哪些state发生变化时,才重新执行;(收谁的影响)

我们来看下面一个案例:

  • 在这个案例中,我们修改show的值,是不会让useEffect重新被执行的;
import React, { useState, useEffect } from 'react';

export default function EffectPerformance() {
  const [count, setCount] = useState(0);
  const [show, setShow] = useState(true);

  useEffect(() => {
    console.log("修改DOM");
  }, [count])

  return (
    <div>
      <h2>当前计数: {count}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      <button onClick={e => setShow(!show)}>切换</button>
    </div>
  )
}

但是,如果一个函数我们不希望依赖任何的内容时,也可以传入一个空数组[]

  • 那么这里两个回到函数分别对应的就是componentDidMountcomponentWillUnmount生命周期函数了
useEffect(() => {
  console.log("监听事件");

  return () => {
    console.log("取消监听");
  }
}, [])
上一篇:什么是react-redux?


下一篇:React Hooks -- useEffect代替常用生命周期函数