先不要看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不刷新。
相關文章
相關標籤/搜索