最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 先不要看HOOKS原理了,你真的知道useEffect运行的顺序吗?

    正文概述 掘金(吃山鬼的神仙)   2021-02-04   483

    起因

    事情是这样的,昨天一个组件useMemo依赖太多了,被我一怒之下删掉了,然后就发现组件渲染次数直线上升,我突然顿悟了,我对react hooks一无所知, 然后本着我自己知道的渲染逻辑,写了几个demo,打印完日志之后才发现,我何止一无所知,我写的是什么玩意??

    锁匠: 您配钥匙吗?我: 我不配

    您配不配钥匙?

    下方代码比较多, 但都是无脑的简单代码,还请按照你的理解,跟着我走一遍,看看是不是跟你理解的一样,我是被按在地上打了个细碎

    先来看看同步场景

    export default function Effect() {
    const [name1, updateName1] = useState('name1');
    const [name2, updateName2] = useState('name2');
    const [name3, updateName3] = useState('name3');
    
    useEffect(() => {
        console.log('---1---');
        updateName1('name1111');
        console.log('---4---');
        updateName2('name2222');
        console.log('---3---');
        updateName3('name3333');
    }, []);
    
    console.log('---render---');
    
    return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
        <div>name1: {name1}</div>\
        <div>name4: {name2}</div>
        <div>name3: {name3}</div>
    </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('---1---');
    console.log('---4---');
    console.log('---3---');
    console.log('---render---');
    

    先来看看同步场景中多个Effect

    export default function Effect() {
    const [name1, updateName1] = useState('name1');
    const [name2, updateName2] = useState('name2');
    const [name3, updateName3] = useState('name3');
    
    useEffect(() => {
        console.log('---1---');
        updateName1('name1111');
    }, []);
    
     useEffect(() => {
        console.log('---4---');
        updateName2('name2222');
    }, []);
    
     useEffect(() => {
        console.log('---3---');
        updateName3('name3333');
    }, []);
    
    console.log('---render---');
    
    return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
        <div>name1: {name1}</div>\
        <div>name4: {name2}</div>
        <div>name3: {name3}</div>
    </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('---1---');
    console.log('---4---');
    console.log('---3---');
    console.log('---render---');
    跟写在一个useEffect里面一样
    

    异步场景1: 多个update在异步函数中

    	export default function Effect() {
    const [name1, updateName1] = useState('name1');
    const [name2, updateName2] = useState('name2');
    const [name3, updateName3] = useState('name3');
    
    useEffect(() => {
        setTimeout(() => {
        	console.log('---1---');
            updateName1('name1111');
            console.log('---4---');
            updateName2('name2222');
            console.log('---3---');
            updateName3('name3333');
        }, 100)
    }, []);
    
    console.log('---render---');
    
    return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
        <div>name1: {name1}</div>\
        <div>name4: {name2}</div>
        <div>name3: {name3}</div>
    </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('---1---');
    console.log('---render---');
    console.log('---4---');
    console.log('---render---');
    console.log('---3---');
    console.log('---render---');
    在异步场景中每次update都会触发render,这个和setState在异步场景的处理一致
    

    是不是感觉简单了?就这?

    往后看

    同步场景+依赖

    function Effect() {
      const [name1, updateName1] = useState('name1');
      const [name2, updateName2] = useState('name2');
      const [name3, updateName3] = useState('name3');
      useEffect(() => {
          console.log('---1---');
          updateName1('name1111');
          console.log('---4---');
          updateName2('name2222');
          console.log('---3---');
          updateName3('name3333');
    
      }, []);
    
      useEffect(() => {
          console.log('---5---');
          updateName2('name2222');
      }, [name1]);
    
      useEffect(() => {
          console.log('---6---');
          updateName2('name3333');
      }, [name3]);
    
      console.log('---render---');
    
      return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
          <div>name1: {name1}</div>
          <div>name4: {name2}</div>
          <div>name3: {name3}</div>
      </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('---1---');
    console.log('---4---');
    console.log('---3---');
    console.log('---5---');
    console.log('---6---');
    console.log('---render---');
    console.log('---5---');
    console.log('---6---');
    console.log('---render---');
    你会发现5 6这两步执行了2遍,并且是合并刷新。
    

    异步场景+依赖

     function Effect() {
      const [name1, updateName1] = useState('name1');
      const [name2, updateName2] = useState('name2');
      const [name3, updateName3] = useState('name3');
    
      useEffect(() => {
          console.log('---inner---');
          setTimeout(() => {
              console.log('---1---');
              updateName1('name1111');
              console.log('---4---');
              updateName2('name2222');
              console.log('---3---');
              updateName3('name3333');
          }, 100);
    
      }, []);
    
      useEffect(() => {
          console.log('---5---');
          updateName3('name2222');
      }, [name1]);
    
      useEffect(() => {
          console.log('---6---');
          updateName2('name3333');
      }, [name3]);
    
      console.log('---render---');
    
      return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
          <div>name1: {name1}</div>
          <div>name4: {name2}</div>
          <div>name3: {name3}</div>
      </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('---inner---');
    console.log('---5---');
    console.log('---6---');
    console.log('---render---');
    console.log('---6---');
    console.log('---render---');
    console.log('---1---');
    console.log('---render---');
    console.log('---4---');
    console.log('---5---');
    console.log('---render---');
    console.log('---3---');
    console.log('---render---');
    console.log('---6---');
    console.log('---render---');
    

    这里问题出来了,4和5怎么被合并了????这是2个Effect里面的东西啊, 4是在异步状态里面,按照异步场景1: 多个update在异步函数中这个检测结果,在异步中的事件应该是每update一次,页面就render一次,现在却是4/5合并了!!

    这里--5--这个effect里面正好依赖了异步中第一次修改的name1, 所以在update name1之后,跟随下一次update,并且在下一次update之后处理了副作用所以就有了先打印4再打印5, 如果吧--6--里面的依赖改成name2那么在updateName2之后3和6也会被合并

    总结: 在异步操作中,每次update之后的副作用会跟着下一次update执行,并且在下一次update之后执行

    异步+依赖场景2 有一次依赖直接被忽略了,没有执行

    export default function ToastPage() {
      const [name1, updateName1] = useState('name1');
      const [name2, updateName2] = useState('name2');
      const [name3, updateName3] = useState('name3');
      const [name4, updateName4] = useState('name4');
    
      useEffect(() => {
          console.log('----第一次变更---');
          setTimeout(() => {
              console.log('---1---');
              updateName1('name1111');
              console.log('---2---');
              updateName4('name1444444');
              console.log('---3---');
              updateName3('name133333');
          }, 100);
    
      }, []);
    
    
      useEffect(() => {
          console.log('---4---');
          updateName3('name333333');
          console.log('---5---');
          updateName4('name544444');
      }, [name1]);
    
      useEffect(() => {
          console.log('---6---');
          updateName2('name344444');
      }, [name4]);
    
    
      console.log('---render---');
    
    
      return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
          <div>name1: {name1}</div>
          <div>name3: {name3}</div>
          <div>name4: {name4}</div>
      </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('----第一次变更---');
    console.log('---4---');
    console.log('---5---');
    console.log('---6---');
    console.log('---render---');
    console.log('---6---');
    console.log('---render---');
    console.log('---1---');
    console.log('---render---');
    console.log('---2---');
    console.log('---4---');
    console.log('---5---');
    // 问题出在这里,执行5之后其实是update了name4 除此之外执行2的时候也更新了name4,但是下方依赖name4变更的6却没有被执行, 而是直接下一步执行了3 然后render就结束了??
    console.log('---render---');
    console.log('---3---');
    console.log('---render---');
    
    上面这个流程,如果把   console.log('---5---'); updateName4('name544444');这两行删掉, --6--这个依赖就会正常执行。
    
    我也不知道为什么...
    

    异步套异步会怎么样?

    function ToastPage() {
      const [name1, updateName1] = useState('name1');
      const [name2, updateName2] = useState('name2');
      const [name3, updateName3] = useState('name3');
      const [name4, updateName4] = useState('name4');
    
      useEffect(() => {
          console.log('----第一次变更---');
          setTimeout(() => {
              console.log('---1---');
              updateName1('name1111');
              console.log('---2---');
              updateName4('name1444444');
              console.log('---3---');
              updateName3('name133333');
          }, 100);
    
      }, []);
    
    
      useEffect(() => {
          console.log('----变更了name1---');
          setTimeout(() => {
              console.log('---4---');
              updateName3('name333333');
              console.log('---5---');
              updateName4('name544444');
          }, 100);
      }, [name1]);
    
      useEffect(() => {
          console.log('---6---');
          updateName2('name344444');
      }, [name4]);
    
    
      console.log('---render---');
    
    
      return <div style={{ marginTop: '100px' }} className="doc-ui-btns">
          <div>name1: {name1}</div>
          <div>name1: {name2}</div>
          <div>name3: {name3}</div>
          <div>name4: {name4}</div>
      </div>;
    }
    
    点击查看执行结果
    console.log('---render---');
    console.log('----第一次变更---');
    console.log('----变更了name1---');
    console.log('---6---');
    console.log('---render---');
    //上面是第一阶段,执行完了同步的effect,下面开始执行异步
    console.log('---1---');
    console.log('---render---');
    console.log('---2---');// 这2个合并,
    console.log('----变更了name1---'); // 但是合并之后的effect是异步,所以只打印了入口
    console.log('---render---'); // 合并之后render
    console.log('---3---'); 3 6合并,这时候依然在处理第一个异步
    console.log('---6---');
    console.log('---render---'); // 合并之后处理异步
    console.log('---4---'); // 开始处理第二个异步,这个其实是第一轮执行useEffect的时候产生的
    console.log('---render---'); // 遵循了 update一次render一次
    console.log('---5---');
    console.log('---render---');
    console.log('---6---');
    console.log('---render---');
    console.log('---4---'); // 这是name1变更后又进来了副作用产生的异步操作,但是一次都没有render
    console.log('---5---');
    没有render
    

    总结一下

    上面的执行逻辑中还是有规律可寻的

    • 在第一次render结束之后,会执行所有的effect,并且合并同步更新
    • 如果在同步的effect中触发了新的同步effect那么会合并更新
    • 在异步中,每次update都会触发页面的render
    • 在异步中,如果每次update产生了副作用,那么副作用会在下一次update之后合并更新
    • 如果是异步update产生的一个异步的effect,那么得等当前所有异步的更新结束之后,才会触发下一次异步事件,这里面就会出现生成多次的情况

    我无法理解的场景

    • 异步+依赖场景2中,有一次effect为什么会被忽略掉
    • 异步套异步会怎么样?中,为什么产生的异步副作用会只update不刷新。

    起源地下载网 » 先不要看HOOKS原理了,你真的知道useEffect运行的顺序吗?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元