React(4)——useEffect

5 阅读2分钟

useEffect

useEffect 我认为是React开发过程中最需要深入理解的hooks,这有助于我们写出更干净的React代码; React官网链接会把useEffect列为逃生舱范围的api,并给出了大量使用useEffect的例子在React的思想中,一个干净的函数应该是输入和输出一致的,但是真实的开发并不是总能这样,所以需要这样的一个副作用函数来帮助我们解决一些额外的问题 在开发的过程中尽可能避免减少使用useEffect(这里网上也有很多文章讲), 只有不可避免的数据请求,事件监听 甚至dom操作才应该在useEffect中使用; 其他的一切应该通过函数式开发来完成,如果你发现一些操作不可避免的需要使用useEffect通常是架构设计出了问题;

现在我们来正式介绍useEffect

useEffect本身可以看作一个闭包 并不会跟着组件更新而发生引用的变化 除非依赖发生改变; 如果你不理解 这里建议结合miniuseState源码和 Dan的文章结合来看。

每次组件渲染后 React 会先执行所有 useEffect 的清理函数(即 return 的函数,如果有的话),清理的是上一次渲染的副作用,然后才会执行本次渲染的 useEffect 回调函数。

useEffect(
    // 其次执行 
    ()=>{

    // 渲染首先执行
    return ()=>{}
},[])

从源码的角度来分析useEffect

初始化

function mountEffect(create,deps){
    const hook = mountWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    currentlyRenderingFiber.effectTag |= UpdateEffect | PassiveEffect;
    hook.memoizedState = pushEffect( 
      HookHasEffect | hookEffectTag, 
      create, // useEffect 第一次参数,就是副作用函数
      undefined, 
      nextDeps, // useEffect 第二次参数,deps    
    )
}

更新

function updateEffect(create,deps){
    const hook = updateWorkInProgressHook();
    if (areHookInputsEqual(nextDeps, prevDeps)) { /* 如果deps项没有发生变化,那么更新effect list就可以了,无须设置 HookHasEffect */
        pushEffect(hookEffectTag, create, destroy, nextDeps);
        return;
    } 
    /* 如果deps依赖项发生改变,赋予 effectTag ,在commit节点,就会再次执行我们的effect  */
    currentlyRenderingFiber.effectTag |= fiberEffectTag
    hook.memoizedState = pushEffect(HookHasEffect | hookEffectTag,create,destroy,nextDeps)
}

再推荐下我认为讲解useEffect使用最好的文章 dan的useEffect