先不要看HOOKS原理了,你真的知道useEffect运行的顺序吗?

原由

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

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

您配不配钥匙?

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

先来看看同步场景

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也会被合并ui

总结: 在异步操做中,每次update以后的反作用会跟着下一次update执行,而且在下一次update以后执行url

异步+依赖场景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
复制代码

总结一下

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

  • 在第一次render结束以后,会执行全部的effect,而且合并同步更新
  • 若是在同步的effect中触发了新的同步effect那么会合并更新
  • 在异步中,每次update都会触发页面的render
  • 在异步中,若是每次update产生了反作用,那么反作用会在下一次update以后合并更新
  • 若是是异步update产生的一个异步的effect,那么得等当前全部异步的更新结束以后,才会触发下一次异步事件,这里面就会出现生成屡次的状况

我没法理解的场景

  • 异步+依赖场景2中,有一次effect为何会被忽略掉
  • 异步套异步会怎么样?中,为何产生的异步反作用会只update不刷新。
相关文章
相关标签/搜索