15-useEffect

115 阅读1分钟

useEffect

  • 用于处理函数组件中的副作用

副作用

  1. ajax请求
  2. 计时器
  3. 更改真实的DOM
  4. 本地存储
  5. 其它会对外部产生影响的操作
  • 类组件中使用副作用的生命周期
    • componentDidUpdate、componentDidUpdate、componentWillMount
  • useEffect:
    • 本质是一个函数
    • 该函数接受一个函数作为参数,接收的函数就是需要进行副作用的函数
import React, { useEffect, useState } from 'react'

function EffectHook() {
  const [n, setN] = useState(0)
  useEffect(() => {
    console.log('这里的是副作用函数')
    document.title = `计数: ${n}`
  })
  return (
    <div>
      <h2>{ n }</h2>
      <button onClick={() => {
        setN(n + 1)
      }} >+1</button>
    </div>
  )
}

export default EffectHook

image.png

细节

  1. 副作用函数的运行时间,是在页面完成真实的UI渲染后.因此它的执行方式是异步的,不会阻塞浏览器
  2. 与 componentDidUpdate、componentDidUpdate区别
    • 2.1 这2个更改了真实DOM,但是用户还没看到UI更新,同步的
    • 2.2 useEffect中的副作用函数,更改了真实的DOM,并且用户已经看到了UI更新,异步的
  3. 没个函数组件中可以多次使用useEffect,但是不要放入判断或者循环的代码块中
  4. useEffect中的副作用函数,可以有返回值,返回值必须是一个函数,(该函数叫做清理函数)
      1. 该函数运行时间点,在每次运行副作用函数之前
      1. 首次渲染不会运行
      1. 组件销毁时候一定会运行
  5. useEffect可以传第二个参数
      1. 第二个参数是一个数组
      1. 数组中记录副作用的依赖数据
      1. 当组件重新渲染后,只有依赖数据与上一次不一样是才会执行副作用
      1. 所以,当传递了依赖数据之后,如果数据没发生变化
      • 副作用函数仅在第一次渲染后运行
      • 清理函数仅在卸载组件后执行
      1. 仅挂载执行一次,仅卸载时候执行一次
      • useEffect(() => {...}, [])

例子移动的块

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

let ref = React.createRef(null)
let timer = null
/**
 * 一个移动块
 * @param {*} props
 * left: 移动到的横坐标点
 * top: 移动到的纵坐标点 
 * @returns 
 */
function MovableBlock(props) {
  useEffect(() => {
    clearInterval(timer);
    const div = ref.current;
    let curTime = 0;//移动的总次数
    const disX = props.left / 100;// 横坐标每次中移动的距离
    const disY = props.top / 100; // 纵坐标每次中移动的距离

    // 控制移动
    timer = setInterval(() => {
      curTime++;
      const newLeft = curTime * disX;
      const newTop = curTime * disY;
      div.style.left = `${newLeft}px`
      div.style.top = `${newTop}px`
      if (curTime === 100) {
        clearInterval(timer);
      }
    }, 100)
    // => 清理函数
    return () => {
      console.log('清理函数触发');
      clearInterval(timer);
    }
  })
  return (
    <div ref={ref} style={{
      width: '100px',
      height: '100px',
      position: 'fixed',
      background: '#f40'
    }}></div>
  )
}

function EffectHook() {
  const [point, setPoint] = useState({
    x: 0,
    y: 0
  })
  const [visible, setVisible] = useState(true)
  return (
    <div>
      {
        visible && (
          <div style={{
            position: 'fixed',
            left: 200
          }}>
            x: <input type="number" value={point.x} onChange={(e) => {
              setPoint({
                ...point,
                x: e.target.value
              })
            }} />
            <br />
            y: <input type="number" value={point.y} onChange={(e) => {
              setPoint({
                ...point,
                y: e.target.value
              })
            }} />
            <MovableBlock left={point.x} top={point.y}></MovableBlock>
          </div>
        )
      }
      <button onClick={() => {
        setVisible(!visible)
      }}>显示/隐藏</button>
    </div>
  )
}

export default EffectHook

image.png image.png

定时器案例

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

function EffectHook() {
  const [n, setN] = useState(10)
  useEffect(() => {
    if (n === 0) return
    // => 某一次渲染完成后,需要更据当前n的值,1秒后重新渲染
    setTimeout(() => {
      setN(n - 1)
    }, 1000)
  }, [n])
  return (
    <div>
      <span>{n}</span>
    </div>
  )
}

export default EffectHook

image.png