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 中。
相关推荐
疯狂的沙粒31 分钟前
如何在 React 项目中应用 TypeScript?应该注意那些点?结合实际项目示例及代码进行讲解!
react.js·typescript
鑫宝Code1 小时前
【React】React Router:深入理解前端路由的工作原理
前端·react.js·前端框架
沉默璇年10 小时前
react中useMemo的使用场景
前端·react.js·前端框架
红绿鲤鱼12 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
loey_ln14 小时前
FIber + webWorker
javascript·react.js
zhenryx14 小时前
前端-react(class组件和Hooks)
前端·react.js·前端框架
老码沉思录18 小时前
React Native 全栈开发实战班 - 性能与调试之打包与发布
javascript·react native·react.js
沉默璇年1 天前
react中Fragment的使用场景
前端·react.js·前端框架
GISer_Jing1 天前
React渲染流程与更新diff算法
前端·javascript·react.js
老码沉思录1 天前
React Native 全栈开发实战班 - 性能与调试之内存管理
javascript·react native·react.js