Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

reactjs

say I have a function like this:

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
};

The problem is that args is a new array each time so infinite re-render happens if it is in the deps array. I do want it to re-render if an element changes.

same goes for the fn function, it is new each time so it will cause infinite re-render, what can I do.

I could stop the hooks eslint rule applying the fix

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

but this seems like a common problem

like image 888
dagda1 Avatar asked Jul 25 '19 12:07

dagda1


1 Answers

According to your needs, I divide the dependency into two parts for analysis.

  1. args

args is an array and will be brand new each time.

Consider creating a useCustorCompareEffect. By customizing the comparison function. The effect will actually be triggered only when the specified value changes.

Edit useCustorCompareEffect Demo

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

To use a function as a dependency, you can wrap the function definition in useCallback and then define the dependencies required by this function in useCallback.


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

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

In your case, your function is obtained by another useWrappedRemoteCall, which needs to use the useCallback package execute definition in useWrappedRemoteCall and then return. In addition, if you only use execute in useEffect, you need to extract execute and then use execute as a dependency. To prevent other data changes in fn from triggering effect

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

If there are other special circumstances, please let me know.

like image 186
XYShaoKang Avatar answered Oct 16 '22 14:10

XYShaoKang