Ant Design Pro V5精讲(基础篇四):useEffect

1,827 阅读4分钟

背景

    useEffect翻译过来就是副作用函数(建议用英文名,不翻译(叫use-ɪˈfekt),更好理解)类组件有各个生命周期,我们喜欢把业务逻辑写在各个生命周期函数中,而函数组件是通过useEffect来实现componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合,优势就是改变原来需要在各个生命周期写业务逻辑的习惯,有时候是重复的。 工作原理:effect执行机制就是比较两次依赖项是相同,不同执行相关effect,如果没有这个依赖项,则是比较两次的effect。

特性

  • useEffect有两个参数,第一个参数为函数,第二个参数一个数组,同时还可以可选再配一个return 函数。 -
  • 第一个参数为一个函数:每次渲染后即DOM更新后,才执行,这个执行指就是这个函数会执行即useEffect默认在每次渲染之后都第一个参数(即函数)会执行,但是可以手动控制它是否执行。这个函数可以放在effect内部,也可以放在外部,但放在useEffect外部同时记得要用useCallback包一下,具体原因需要了解useCallback的作用,这个函数尽量不要嵌套依赖太多。官方还是建议放在useEffect体的内部,当函数嵌套依赖代码量过大时这样才能保证准确更新相关effect的依赖。
  • 第二个参数为一个值数组:忘记组件的生命周期函数,通过第二个参数的变化实现不同的功能,达到生命周期函数同样的效果,不用再去考虑挂载还是更新,只需要接受“组件渲染到屏幕之后执行”这个概念,它具有通过useEffect同步获取render后的props和state的概念;第二个参数的空数组[]时,表示告诉useEffect不依赖于state,props中的任意值,这时候useEffect只会运行一次;如果数组中有值,表示当这个state或者props有值,才去执行这个useEffect中的第一个参数即函数。
  • 清除函数:即有一个return函数,它是在第二次effect执行前需要运行的内容。 使用必须遵守hook的规则。

用法

语法:

useEffect(() => {},[])

1.组件挂载时需要初始工作,即只执行一次(相当于componentDidMount):第二个参数必须为空的数组,即[]

useEffect( () => { return () => { .... }; }, [], );

2.组件销毁时执行,相当于componentWillUnmount 

把销毁时需要执行的业务逻辑写在上面的return{}中,即必须有return。

为提升性能阻止每次渲染后都会执行useEffect, 相当于componentDidUpdate

3.第二个参数加一个值数组,即 [value],只有这个值发变化时,才会执行useEffect中定义的业务逻辑。

const [value, setValue] = useState('initial');

useEffect(() => { // 仅在 value 更改时更新 console.log(value); }, [value]) 每次页面组件渲染均执行

4.不设置第二个参数,每次渲染时均执行。

useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除订阅 subscription.unsubscribe(); }; });  最经常与异步api函数的搭配使用,例如前端调用服务器api上取数据,有三种方法,这边只介绍我个人习惯的一种方法:

const [tableList, setTableList] = useState(); const fetchMyAPI =async()=> { let res= await fetch('api/getList'); data= await res.json(); setTableList(data); // 或者return XXXX }

useEffect(() => { fetchMyAPI(); }, []);

高级用法

useEffect的第一个参数是函数,这个函数有两种定义风格:

1.函数放在useEffect内部:这个函数体不会被多个useEffect共用时,我个人建议尽量放在函数体内部,这样不用考虑当函数体代码量大时,嵌套层次多时,这样才能保证依赖项变化时,能准确执行effect。

2.函数放在useEffec外部:如果这个函数在多个effect中被复用时,我们必须将函数放在effect外部,针对逻辑漏洞和代码eslint警告,有两种方法:

  • 使用useCallback hooks进行包装,我个人推荐这个方式,特别适合当这个函数内部要对state进行setState操作时,可以使用useCallback进行包装成一个依赖state可变值的函数,然后把这个函数作为useEffect的依赖使用,例如:
const countTest = useCallback(() => {
    return setTimeout(() => {
        setCount(c => c+10)
    },1000)
},[])
 
useEffect(() => {
   const fn = countTest()
   return () => clearTimeout(fn)
},[countTest])
console.log(count)
  • 将函数转化成纯函数,移到组件外部,前提是这个函数没有用到组件内部的state或者props时。

小结:

  • 第二个参数不指定依赖时,useEffect每次都会执行第一个参数函数的内容 。
  • 第二个参数为空数组时,组件挂载完成后useEffect执行一次第一个参数函数的内容,模拟componentDidMount
  • 第二个具体的参数依赖时,当指定的依赖更新(即state或者props值变化)时可再次执行副作用,可用来模拟componentDidUpdate
  • useEffect的返回函数写的代码内容,只有在组件销毁时才去执行(即effect第二次执行前),用来模拟componentWillUnmount