Hook 学习系列(二) —— Effect Hook


今天学习另一个 Hook: useEffect,它能在函数组件中执行副作用,与 class 中的生命周期函数极为类似。

useEffect

  • useEffect Hook 可以看做 componentDidMount,componentDidUpdatecomponentWillUnmount 这三个函数的组合。
  • react 首次渲染和之后的每次渲染都会调用一遍传给 useEffect 的函数。之前要用两个声明周期函数来分别表示首次渲染(componentDidMount),和之后的更新导致的重新渲染(componentDidUpdate)
  • useEffect 中定义的副作用函数的执行不会阻碍浏览器更新视图,也就是说这些函数是异步执行的,之前的 componentDidMountcomponentDidUpdate 中的代码则是同步执行的。

例子:

import React, { useState, useEffect } from 'react'
function Example () {
  const [count, setCount] = useState(0)
  const [age, setAge] = useState(42)
  useEffect(() => {
    // 将 document 的 title 设置为包含了点击次数的消息
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>

      <p>setAge: {age} </p>
      <button onClick={() => setAge(age + 3)}>
        Click me
      </button>
    </div>
  )
}
export default Example

等价于:

import React from 'react'

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      age: 10
    };
  }

  componentDidMount () {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate () {
    document.title = `You clicked ${this.state.count} times`;
  }
  render () {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({
          count: this.state.count + 1
        })}>
          Click me
        </button>


        <p>setAge: {this.state.age} </p>
        <button onClick={() => this.setState({
          age: this.state.age + 3
        })}>
          Click me
        </button>

      </div>
    );
  }
}
export default Example

页面效果:
请添加图片描述

effect 中可以直接访问 count state 变量(或其他 props),它已经保存在函数作用域中。(Hook 使用了 JavaScript 的闭包机制)
默认情况下,effect 在第一次渲染之后和每次更新之后都会执行。effect 发生在“渲染之后”,可以不用再去考虑“挂载”还是“更新”。
React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。每次重新渲染,都会生成新的 effect,替换掉之前的。(也就是说,每个 effect “属于”一次特定的渲染)

清除 effect

如果 effect 返回一个函数,React 将会在执行清除操作时调用它。

每个 effect 都可以返回一个清除函数,可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。 React 会在组件卸载的时候执行清除操作。

例子:

  useEffect(() => {
    document.title = `You clicked ${count} times`;
    return () => {
      // 执行清除操作
    }
  })

不必做清除操作的 effect 所以不需要返回函数,例如:

 useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

多个effect

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

useEffect(() => {
  document.title = `You clicked ${count} times`;
})
useEffect(() => {
  console.log('倒计时完毕!')
})

React 将按照 effect 声明的顺序依次调用组件中的每一个 effect

useEffect 进行性能优化

跳过 effect

如果某些特定值在两次重渲染之间没有发生变化,可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:

  useEffect(() => {
    // 将 document 的 title 设置为包含了点击次数的消息
    document.title = `You clicked ${count} times`;
  }, [count]) // 仅在 count 更改时更新

上面这个例子中,传入 [count] 作为第二个参数,来实现性能优化。

具体实现:

如果 count 的值是 5,而且组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。

例子:

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

let timer; //声明定时器

const Example = () => {
  const [current, setCurrent] = useState(0)
  const [time, setTime] = useState(5)
  useEffect(() => {
    return () => {
      clearInterval(timer)
    }
  }, [])
  // 想执行只运行一次的 effect(仅在组件挂载和卸载时执行),传递一个空数组([])作为第二个参数。
  // 告诉 React,effect 不依赖于 props 或 state 中的任何值,永远都不需要重复执行。
  useEffect(() => {
    if (current === 1) {
      runTimerJump()
    }
  }, [current])
  useEffect(() => {
    if (time === 0) {
      clearInterval(timer)
      setTime(5)
      alert('倒计时完毕!')
      console.log('倒计时完毕!')
    }
  }, [time])
  const runTimerJump = () => {
    timer = setInterval(() => setTime(t => --t), 1000)
  }
  return (
    <>
      <p>
        <span>{time}s 后执行对应的操作</span>
        <br />
      </p>
      <p><button onClick={() => setCurrent(1)}>点击开启</button></p>
    </>
  )
}
export default Example

页面效果:
请添加图片描述

仅执行一次的 effect

以上例子中,useEffect 中 的 return 是返回来一个函数,来清除定时器:

useEffect(() => {
    return () => {
      clearInterval(timer)
    }
  }, [])
  // 想执行只运行一次的 effect(仅在组件挂载和卸载时执行),传递一个空数组([])作为第二个参数。
  // 告诉 React,effect 不依赖于 props 或 state 中的任何值,永远都不需要重复执行。

传递一个空数组([])作为第二个参数,effect 内部的 propsstate 会一直拥有其初始值,告诉 Reacteffect 不依赖于 propsstate 中的任何值,永远都不需要重复执行。

适用情形:想执行只运行一次的 effect(仅在组件挂载和卸载时执行)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值