簡體   English   中英

如何在useeffect中處理rest參數和函數導致重新渲染

[英]how to handle rest arguments and function in useeffect causing re-render

說我有這樣的功能:

export const usePagedGetAll = <R, Args extends any[]>(
  baseUrl: string,
  ...args: Args
) => {
  const fn = useWrappedRemoteCall(
    async () => {
      return await fetchRequest<RemoteData<R>>(url, options);
    }
  );

  useEffect(() => {
    (async () => {
      await fn.execute(...args);
    })();
  }, [args, fn, paginationInfo.skip, paginationInfo.take]);

  return fn
};

問題是args每次都是一個新的數組,所以如果它在deps數組中就會發生無限的重新渲染。 如果元素發生變化,我確實希望它重新渲染。

同樣適用於fn函數,它每次都是新的,因此它將導致無限重新渲染,我該怎么辦。

我可以停止應用修復的鈎子eslint規則

  useEffect(() => {
    (async () => {
      await fn.execute(...args);
    })();
    // eslint-disable-next-line
  }, [paginationInfo.skip, paginationInfo.take]);

但這似乎是一個普遍的問題

根據您的需要,我將依賴關系分為兩部分進行分析。

  1. ARGS

args是一個數組,每次都是全新的。

考慮創建一個useCustorCompareEffect 通過自定義比較功能。 僅當指定的值更改時,才會觸發effect

編輯useCustorCompareEffect演示

const useCustorCompareEffect = (callback, value, compare) => {
  const prev = useRef({ init: true, value })
  useEffect(() => {
    const { current } = prev
    if (current.init) {
      callback()
      current.init = false
    } else {
      if (!compare || !compare(value, current.value)) {
        callback()
        current.value = value
      }
    }
  }, [callback, value, compare])
}

const useDemo=()=>{
  const compare = useCallback((curr, prev) => {
    // Compare functions, return comparison results, use `useCallback` to prevent triggering `effect` due to compare
  }, [])

  useCustorCompareEffect(
    () => {
      ...
    },
    [data],
    compare,
  )
}
  1. FN

要將函數用作依賴項,可以將函數定義包裝在useCallback ,然后在useCallback定義此函數所需的依賴useCallback


const execute = useCallback(() => {
  console.log('update')
}, [])

useEffect(() => {
  execute()
}, [execute);

在你的情況下,被另一個獲得你的函數useWrappedRemoteCall ,這需要使用useCallbackexecute的定義useWrappedRemoteCall ,然后返回。 另外,如果只在useEffect使用executeuseEffect需要提取execute然后使用execute作為依賴項。 防止fn中的其他數據更改觸發effect

const useWrappedRemoteCall = () => {
  const execute = useCallback(() => {
    console.log('update')
  }, [])
  return { execute }
}
const usePagedGetAll = () => {
  const { execute } = useWrappedRemoteCall()
  useEffect(() => {
    execute()
  }, [execute])
}

如果還有其他特殊情況,請告訴我。

您可以使用useRef編寫一個自定義鈎子來深入檢查args

function usePrevious(value) {
  const ref = React.useRef(null);
  if(value != ref.current) {
    // ...
    // check if they are the same here else update ref.current
    // ...
    ref.current = value;
  }
  return ref.current;
}

然后在useMemo包裝useWrappedRemoteCall函數,因為它返回一個對象。

const fn = React.useMemo(() => useWrappedRemoteCall(
  async () => {
    return await fetchRequest<RemoteData<R>>(url, options);
  }
), [url, options, fetchRequest]);

最后結果:

export const usePagedGetAll = <R, Args extends any[]>(
  baseUrl: string,
  ...args: Args
) => {

  // use previous args if unmodified
  args = usePrevious(args);

  // useMemo to memoize the object returned by useWrappedRemoteCall
  const fn = React.useMemo(() => useWrappedRemoteCall(
    async () => {
      return await fetchRequest<RemoteData<R>>(url, options);
    }
  ), [url, options, fetchRequest]);

  React.useEffect(() => {
    (async () => {
      await fn.execute(...args);
    })();
  }, [args, fn, paginationInfo.skip, paginationInfo.take]);

  return fn
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM