React hooks - useEffect

用法
  1. 执行副作用操作,例如:请求数据、事件监听
    函数副作用定义:除了返回值外函数对外界环境造成的其它影响,如获取数据、修改全局变量、更新 DOM。
useEffect(fn, deps?)

// 第一个参数 fn 是一个副作用函数,该函数会在每次渲染完成之后被调用
// 第二个参数是可选的依赖项数组,这个数组中的每一项内容都会被用来进行渲染前后的对比,依赖项发生变化时,才会重新执行副作用函数

第二个参数的用法
  1. 不指定依赖项:函数组件每次渲染完成后执行
  2. 依赖项为空数组:函数组件首次渲染完成后执行一次
  3. 依赖项为数组:函数组件每次渲染完成,对比渲染前后依赖项是否发生变化,只要变化,副作用函数就重新执行
import React, { useEffect, useState } from 'react'

export const Counter: React.FC = () => {
  const [count, setCount] = useState(0)
  const [flag, setFlag] = useState(false)

  const add = () => {
    setCount((prev) => prev + 1)
  }

  // 在组件每次渲染完成后,如果 count 值发生了变化,则执行 effect 中的回调
  // 其它状态的变化,不会导致此回调函数的重新执行
  useEffect(() => {
    console.log(document.querySelector('h1')?.innerHTML)
  }, [count])

  return (
    <>
      <h1>count 值为:{count}</h1>
      <p>flag 的值为:{String(flag)}</p>
      <button onClick={add}>+1</button>
      <button onClick={() => setFlag((prev) => !prev)}>Toggle</button>
    </>
  )
}
清理副作用

实际应用:当前组件中使用了定时器或绑定了事件监听程序,可以在返回的函数中清除定时器或解绑监听程序

清理函数的2个触发时机:

  1. 组件被卸载的时候,会调用
  2. 当 effect 副作用函数被再次执行之前,会先执行清理函数
useEffect(() => {
  // 1. 执行副作用操作
  // 2. 返回一个清理副作用的函数
  return () => { /* 在这里执行自己的清理操作 */ }
}, [依赖项])
组件卸载时终止未完成的 Ajax 请求

AbortController 可用于中止fetch请求
创建对象:const controller = new AbortController()
将中止信号与请求绑定:fetch( ‘请求路径’ , { signal: controller.signal })
中止请求:controller.abort()

const RandomColor: React.FC = () => {
  const [color, setColor] = useState('')

  useEffect(() => {
    const controller = new AbortController() 

    fetch('https://api.kaka.com/v1/color', { signal: controller.signal })
      .then((res) => res.json())
      .then((res) => {
        console.log(res)
        setColor(res.data.color)
      })
      .catch((err) => console.log('消息:' + err.message))

    // return 清理函数
    return () => controller.abort()
  }, [])

  return (
    <>
      <p>color 的颜色值是:{color}</p>
    </>
  )
} 
获取鼠标在网页中移动时的位置

如果不在组件卸载时清理定时器,下一次再显示时会再创建一个新的定时器,就会有两个定时器都打印位置

const MouseInfo: React.FC = () => {
  // 鼠标的位置
  const [position, setPosition] = useState({ x: 0, y: 0 })

  useEffect(() => {
    // 使用setTimeout定时器,给鼠标操作添加节流操作,500s才执行一次
    let timeId : null | NodeJS.Timeout = null
    // 1. 要绑定或解绑的 mousemove 事件处理函数,MouseEvent类型
    const mouseMoveHandler = (e: MouseEvent) => {
      if(timeId !== null) return // 定时器id不为null,证明正在执行,直接return
      timeId = setTimeout(() => {
         console.log({ x: e.clientX, y: e.clientY })
         setPosition({ x: e.clientX, y: e.clientY })
         timeId = null // 为开启下一次延时器做准备
      },500)
    }

    // 2. 组件首次渲染完毕后,为 window 对象绑定 mousemove 事件
    window.addEventListener('mousemove', mouseMoveHandler)

    // 3. 返回一个清理的函数,在每次组件卸载时,为 window 对象解绑 mousemove 事件
    return () => window.removeEventListener('mousemove', mouseMoveHandler)
  }, [])

  return (
    <>
      <p>鼠标的位置:{JSON.stringify(position)}</p>
    </>
  )
}
export const TestMouseInfo: React.FC = () => {
  // 控制子组件的显示或隐藏
  const [flag, setFlag] = useState(true)

  return (
    <>
      <h3>父组件</h3>
      {/* 点击按钮,切换 flag 的值 */}
      <button onClick={() => setFlag((prev) => !prev)}>Toggle</button>
      <hr />
      {flag && <MouseInfo />}
    </>
  )
} 
注意事项
  1. 不建议把对象作为 useEffect 的依赖项,因为 React 使用 Object.is() 来判断依赖项是否发生变化。
  2. 若未设置依赖项,不要在 useEffect 中改变依赖项的值,会造成死循环,因为状态更新都要触发useEffect。
  3. 多个不同功能的副作用尽量分开声明,不要写到一个 useEffect 中。
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值