精讀《use-what-changed 源碼》

1 引言

使用 React Hooks 的時候,常常出現執行次數過多甚至死循環的狀況,咱們能夠利用 use-what-changed 進行依賴分析,找到哪一個變量引用一直在變化。前端

據一個例子,好比你嘗試在 Class 組件內部渲染 Function 組件,Class 組件是這麼寫的:git

class Parent extends React.PureComponent {
  render() {
    return <Child style={{ color: "red" }} />;
  }
}

子組件是這麼寫的:github

const Child = ({ style }) => {
  const [localStyle, setLocalStyle] = useState();

  useEffect(() => {
    setLocalStyle(style);
  }, [style]);

  return null;
};

那麼恭喜你,寫出了一個最簡單的死循環。這個場景裏,咱們本意是利用 useEffectprops.style 同步到本地狀態 localStyle 中,但執行 setLocalStyle 會致使當前組件重渲染,因爲父級 style={{ color: "red" }} 的寫法,每次重渲染拿到的 props.style 引用都會變化,所以再次觸發了 useEffect 回調執行,進而再次執行到 setLocalStyle 觸發死循環。數組

僅僅打印出值是看不出變化的,引用的改變很隱蔽,爲了判斷是否變化還得存儲上一次的值作比較,很是麻煩,use-what-changed 就是爲了解決這個麻煩的。微信

2 精讀

use-what-changed 使用方式以下:babel

function App() {
  useWhatChanged([a, b, c, d]); // debugs the below useEffect

  React.useEffect(() => {
    // console.log("some thing changed , need to figure out")
  }, [a, b, c, d]);
}

將參數像依賴數組同樣傳入,刷新頁面就能夠在控制檯看到引用或值是否變化,若是變化,對應行會展現 ✅ 並打印出上次的值與當前值:工具

第一步是存儲上一次依賴項的值,利用 useRef 實現:性能

function useWhatChanged(dependency?: any[]) {
  const dependencyRef = React.useRef(dependency);
}

而後利用 useEffect,對比 dependencydependencyRef 的引用便可找到變化項:spa

React.useEffect(() => {
  let changed = false;
  const whatChanged = dependency
    ? dependency.reduce((acc, dep, index) => {
        if (dependencyRef.current && dep !== dependencyRef.current[index]) {
          changed = true;

          const oldValue = dependencyRef.current[index];
          dependencyRef.current[index] = dep;
          acc[`"✅" ${index}`] = {
            "Old Value": getPrintableInfo(oldValue),
            "New Value": getPrintableInfo(dep),
          };

          return acc;
        }

        acc[`"⏺" ${index}`] = {
          "Old Value": getPrintableInfo(dep),
          "New Value": getPrintableInfo(dep),
        };

        return acc;
      }, {})
    : {};

  if (isDevelopment) {
    console.table(whatChanged);
  }
}, [dependency]);
  1. 直接對比 deps 引用,不想等則將 changed 設爲 true。
  2. 調試模式下,利用 console.table 打印出表格。
  3. 依賴項是 dependency,當依賴項變化時纔打印 whatChanged。

以上就是其源碼的核心邏輯,固然咱們還能夠簡化輸出,僅當有引用變化時纔打印表格,不然只輸出簡單的 Log 信息:插件

if (isDevelopment) {
  if (changed) {
    console.table(whatChanged);
  } else {
    console.log(whatChanged);
  }
}

babel 插件

最後 use-what-changed 還提供了 babel 插件,只經過註釋就能打印 useMemouseEffect 等依賴變化信息。babel 配置以下:

{
  "plugins": [
    [
      "@simbathesailor/babel-plugin-use-what-changed",
      {
        "active": process.env.NODE_ENV === "development" // boolean
      }
    ]
  ]
}

使用方式簡化爲:

// uwc-debug
React.useEffect(() => {
  // console.log("some thing changed , need to figure out")
}, [a, b, c, d]);

將 Hooks 的 deps 數組直接轉化爲 use-what-changed 的入參。

3 總結

use-what-changed 補充了 Hooks 依賴變化的調試方法,對於 React 組件重渲染分析能夠利用 React Dev Tool,能夠參考 精讀《React 性能調試》

還有哪些實用的 Hooks 調試工具呢?歡迎分享。

討論地址是: 精讀《use-what-changed 源碼》· Issue #256 · dt-fe/weekly

若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。

關注 前端精讀微信公衆號

版權聲明:自由轉載-非商用-非衍生-保持署名( 創意共享 3.0 許可證

本文使用 mdnice 排版

相關文章
相關標籤/搜索