React Hooks系列之useEffect

前言

react hooks 是 React 16.8 的新增特性。 它可以让我们在函数组件中使用 state 、生命周期以及其他 react特性,而不仅限于 class 组件。react hooks 的出现,标示着 react中不会在存在无状态组件了,只有类组件和函数组件。具体可查看官网

优势:

  1. 函数组件不能使用state,遇到交互更改状态等复杂逻辑时不能更好地支持,hooks让函数组件更靠近class组件,拥抱函数式编程。
  2. 解决副作⽤问题,hooks出现可以处理数据获取、订阅、定时执行任务、手动修改 ReactDOM这些⾏为副作用,进行副作用逻辑。比如useEffect。
  3. 更好写出有状态的逻辑重用组件。
  4. 让复杂逻辑简单化,比如状态管理:useReducer、useContext。
  5. 函数式组件比class组件简洁,开发的体验更好,效率更⾼,性能更好。
  6. 更容易发现无用的状态和函数。

useEffect介绍

useEffect(didUpdate);
拆解
useEffect(callback,array)

useEffect也是componentDidMount、componentDidUpdate和componentWillUnmount这⼏个⽣命周期方法的统⼀,该 Hook 接收一个包含命令式、且可能有副作用代码的函数。

  • callback: 回调函数,作⽤是处理副作⽤逻辑
  • array(可选参数):数组,⽤于控制useEffect的执⾏

在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性,useEffect就是为了处理这些副作用⽽生的。

使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。

默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候才执行。

useEffect使用

简单例子,有这样一个需求,需要我们在组件在状态更新的时候改变 document.title,如下:

 
import {useState,useEffect} from 'react'
const App=() => {
const [count,setState]=useState(0)
useEffect(() =>{ //更新⻚面标题
	document.title=`您点击了了${count}次了了哦` },[count])
    return (
      <div>
	 	<div>你点击了了{count}</div>
		<button onClick={()=>setState(count+1)}>点 击</button>
	  </div> 
	)
}

重点:useEffect 会返回一个回调函数,作用于清除上一次副作用遗留下来的状态,如果该 useEffect 只调用一次,该回调函数相当于 componentWillUnmount 生命周期。

 
useEffect(() =>{ 
	//副作⽤逻辑xxxxxx
	return ()=>{
	//清理副作用需要清理的内容 //类似于componentWillUnmount,组件渲染和组件卸载前执⾏的代码
	}
 },[])

比如:通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数。

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // 清除订阅
    subscription.unsubscribe();
  };
});

为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除。

effect 的条件执行

条件执行其实array(可选参数)数组有关,用于控制useEffect的执行。

  1. 什么都不传,组件每次 render 之后 useEffect 都会调用,相当于 componentDidMount 和 componentDidUpdate。
  2. 传入一个空数组 [], 只会调用一次,相当于 componentDidMount 和 componentWillUnmount。
  3. 传入一个数组,其中包括变量,只有这些变量变动时,useEffect 才会执行。

具体看下面例子

  function App () {
    const [ count, setCount ] = useState(0)
    const [ width, setWidth ] = useState(document.body.clientWidth)

    const onChange = () => {
      
      setWidth(document.body.clientWidth)
    }

    useEffect(() => {
      window.addEventListener('resize', onChange, false)

      return () => {
        window.removeEventListener('resize', onChange, false)
      }
    })

    useEffect(() => {
      document.title = count
    })

    return (
      <div>
        名称: { count } 
        宽度: { width }
        <button onClick={() => { setCount(count + 1)}}>点我</button>
      </div>
      )
  }

这例子要处理两种副作用逻辑,这里我们既要处理 title,还要监听屏幕宽度改变,按照 class 的写法,我们要在生命周期中处理这两种逻辑,但在 hooks 中,我们只需要两个 useEffect 就能解决这些问题,我们之前提到,useEffect 能够返回一个函数,用来清除上一次副作用留下的状态,这个地方我们可以用来解绑事件监听,这个地方存在一个问题,就是 useEffect 是每次 render 之后就会调用,比如 title 的改变,相当于 componentDidUpdate,但我们的事件监听不应该每次 render 之后,进行一次绑定和解绑,就是我们需要 useEffect 变成 componentDidMount, 它的返回函数变成 componentWillUnmount,这里就需要用到 useEffect 函数的第二个参数了。

改写:

  function App () {
    const [ count, setCount ] = useState(0)
    const [ width, setWidth ] = useState(document.body.clientWidth)

    const onChange = () => {
      setWidth(document.body.clientWidth)
    }

    useEffect(() => {
      // 相当于 componentDidMount
      console.log('add resize event')
      window.addEventListener('resize', onChange, false)

      return () => {
        // 相当于 componentWillUnmount
        window.removeEventListener('resize', onChange, false)
      }
    }, [])

    useEffect(() => {
      // 相当于 componentDidUpdate
      document.title = count
    })

    useEffect(() => {
      console.log(`count change: count is ${count}`)
    }, [ count ])

    return (
      <div>
        名称: { count } 
        宽度: { width }
        <button onClick={() => { setCount(count + 1)}}>点我</button>
      </div>
      )
  }

第一个 useEffect 中的 ‘add resize event’ 只会在第一次运行时输出一次,无论组件怎么 render,都不会在输出,第二个 useEffect 会在每次组件 render 之后都执行,title 每次点击都会改变, 第三个 useEffect, 只要有在第一次运行和 count 改变时,才会执行,屏幕发生改变引起的 render 并不会影响第三个 useEffect。

官方推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps
规则。此规则会在添加错误依赖时发出警告并给出修复建议。

发布了82 篇原创文章 · 获赞 46 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_43720095/article/details/104948100