React hooks - useEffect

useEffect

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

// 第一个参数 fn 是一个副作用函数,该函数会在每次渲染完成之后被调用

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

第二个参数的用法
  1. 不指定依赖项:函数组件每次渲染完成后执行
  2. 依赖项为空数组:函数组件首次渲染完成后执行一次
  3. 依赖项为数组:函数组件每次渲染完成,对比渲染前后依赖项是否发生变化,只要变化,副作用函数就重新执行
javascript 复制代码
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 副作用函数被再次执行之前,会先执行清理函数
javascript 复制代码
useEffect(() => {
  // 1. 执行副作用操作
  // 2. 返回一个清理副作用的函数
  return () => { /* 在这里执行自己的清理操作 */ }
}, [依赖项])
组件卸载时终止未完成的 Ajax 请求

AbortController 可用于中止fetch请求

创建对象:const controller = new AbortController()

将中止信号与请求绑定:fetch( '请求路径' , { signal: controller.signal })

中止请求:controller.abort()

javascript 复制代码
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>
    </>
  )
} 
获取鼠标在网页中移动时的位置

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

javascript 复制代码
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>
    </>
  )
}
javascript 复制代码
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 中。
相关推荐
PairsNightRain33 分钟前
React Concurrent Mode 是什么?怎么使用?
前端·react.js·前端框架
小岛前端1 小时前
React 剧变!
前端·react.js·前端框架
用户47949283569154 小时前
面试官:讲讲这段react代码的输出(踩坑)
前端·javascript·react.js
GISer_Jing4 小时前
React中Element、Fiber、createElement和Component关系
前端·react.js·前端框架
lvchaoq16 小时前
react 修复403页面无法在首页跳转问题
前端·javascript·react.js
郝开16 小时前
6. React useState基础使用:useState修改状态的规则;useState修改对象状态的规则
前端·javascript·react.js
Codigger官方17 小时前
Linux 基金会牵头成立 React 基金会:前端开源生态迎来里程碑式变革
linux·前端·react.js
ObjectX前端实验室17 小时前
【图形编辑器架构】🧠 Figma 风格智能选择工具实现原理【猜测】
前端·react.js
技术钱18 小时前
react+andDesign+vite+ts从零搭建后台管理系统(三)-Layout布局
javascript·react.js·ecmascript
郝开18 小时前
7. React组件基础样式控制:行内样式,class类名控制
react.js