日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達者為師

網(wǎng)站首頁 編程語言 正文

React?Hooks?useReducer?逃避deps組件渲染次數(shù)增加陷阱_React

作者:qwer ? 更新時間: 2022-11-01 編程語言

前言

在快樂使用 React Hooks 開發(fā)自定義 Hooks 過程中,使用了 useEffectuseReduceruseRefuseCallback 等官方提供的 Hooks,將一些通用邏輯抽離出來,提高代碼復(fù)用性。

但在組合使用 useEffectuseReducerReact.memo 時,發(fā)生了組件在狀態(tài)未發(fā)生變化時觸發(fā)渲染,因為此動作發(fā)生在 mousemove 鼠標(biāo)移動時,所以組件不必要渲染次數(shù)非常多。

自定義 Hooks 簡單實現(xiàn)

import { useReducer } from "react";
const reducer = (state, action) => {
  const { sliding, lastPos, ratio } = state;
  switch (action.type) {
    case "start":
      return {
        ...state,
        slideRange: action.slideRange,
        lastPos: action.x,
        sliding: true,
      };
    case "move":
      if (!sliding) {
        return state;
      }
      const offsetX = action.x - lastPos;
      const newRatio = ratio + offsetX / state.slideRange;
      if (newRatio > 1 || newRatio < 0) {
        return state;
      }
      return {
        ...state,
        lastPos: action.x,
        ratio: newRatio,
      };
    case "end":
      if (!sliding) {
        return state;
      }
      return {
        ...state,
        sliding: false,
      };
    case "updateRatio":
      return {
        ...state,
        ratio: action.ratio,
      };
    default:
      return state;
  }
};
export function useSlider(initialState) {
    const [state, dispatch] = useReducer(reducer, initialState);
    return [state, dispatch];
}

在組件中使用自定義 Hooks

const [state, dispatch] = useSlider(initialState);
  const { ratio, sliding, lastPos, slideRange } = state;
  useEffect(() => {
    const onSliding = (e) => {
      dispatch({ type: "move", x: e.pageX });
    };
    const onSlideEnd = () => {
      dispatch({ type: "end" });
    };
    document.addEventListener("mousemove", onSliding);
    document.addEventListener("mouseup", onSlideEnd);
    return () => {
      document.removeEventListener("mousemove", onSliding);
      document.removeEventListener("mouseup", onSlideEnd);
    };
  }, [dispatch]);
  const handleThumbMouseDown = useCallback(
    (event) => {
      const hotArea = hotAreaRef.current;
      dispatch({
        type: "start",
        x: event.pageX,
        slideRange: hotArea.clientWidth,
      });
      if (event.target.className !== "point") {
        dispatch({
          type: "updateRatio",
          ratio: (event.pageX - 30) / hotArea.clientWidth,
        });
      }
    },
    [dispatch]
  );

鼠標(biāo)每次移動,都會觸發(fā) dispatch({ type: "move", x: e.pageX }),在 reducer 函數(shù)中,當(dāng) !sliding 時,不修改 state 數(shù)據(jù)原樣返回,但是組件仍然進行了渲染。

提前阻止 dispatch 觸發(fā)

sliding 判斷移動到 useEffect 中,提前阻止 dispatch 觸發(fā),并將 sliding 設(shè)置到 useEffect(fn, deps) deps 中,保證監(jiān)聽函數(shù)中能取到 sliding 最新值。

useEffect(() => {
    const onSliding = (e) => {
      if(!sliding) {
        return;
      }
      dispatch({ type: "move", x: e.pageX });
    };
    const onSlideEnd = () => {
      if (!sliding) {
        return;
      }
      dispatch({ type: "end" });
    };
    document.addEventListener("mousemove", onSliding);
    document.addEventListener("mouseup", onSlideEnd);
    return () => {
      document.removeEventListener("mousemove", onSliding);
      document.removeEventListener("mouseup", onSlideEnd);
    };
  }, [sliding]);

優(yōu)化后再測試

鼠標(biāo)僅移動時,slidingfalse,直接 return,不會觸發(fā) dispatch 動作。

好處

避免了組件在 state 未修改時不必要渲染。

壞處

部分處理邏輯被移動到使用自定義 hooks 的組件中,sliding 數(shù)據(jù)改變時,add EventListener函數(shù)會重新注冊。

結(jié)論

不能為了不在 useEffect(fn, deps) 設(shè)置 deps,使用 useReducer,并把所有數(shù)據(jù)變更都放在 reducer 中。 本篇文章通過把不修改 reducer state 的動作提前阻止,避免使用此自定義 hooks 的組件發(fā)生不必要渲染,提高代碼復(fù)用性的同時也兼顧了組件性能。

題外

  • React.memoprops 進行淺比較,一種組件性能優(yōu)化方式
  • useCallback(fn, deps) 緩存函數(shù)
  • useMemo(() => fn, deps) 緩存昂貴變量
  • useState(initialState)惰性初始stateinitialState` 只會在組件初始渲染中起作用,后續(xù)渲染時會被

參考文獻

React 官方文檔

原文鏈接:https://juejin.cn/post/7139834444630163463

欄目分類
最近更新