skip to Main Content

I have a hook that gets data from another hook

const useIsAdult = () => {
  const data = useFormData();

  return data.age > 18;
}

This hook returns true or false only, however the useFormData is constantly being updated. Everytime useFormData reruns, it will rerender my component. I only want my component to rerender if the result of useIsAdult changes.

I know this can obviously be solved by implementing some logic via react-redux and using their useSelector to prevent rendering, but I’m looking for a more basic solution if there is any.

Before you say useMemo, please read question carefully. The return value is a boolean, memo here doesnt make any sense.

2

Answers


  1. Memoize hook

    const useIsAdult = () => {
      const data = useFormData();
      return useMemo(() => data.age > 18, [data.age]);
    }
    

    Here, useMemo will let you cache the calculation or multiple renderes, by "remembering" previous computation. So, if the dependency (data.age) doesn’t change then it will use simply reuse the last value it returned, the cached one.

    Memoize component expression

    useMemo can also be used to memoize component expressions like this: –

    const renderAge = useMemo(() => <MyAge age={data.age} />, [data.age]);
    return {renderAge}
    

    Here, MyAge will only re-render if the value of data.age changes.

    Login or Signup to reply.
  2. Even with returned useMemo in useIsAdult parent component will be rerendered. This problem is reason why I rarely create custom hooks with other hooks dependency. It will be rerender hell.

    There I tried to show that useMemo doesnt work. And using useMemo for components its wrong way. For components we have React.memo.

    const MyComponent = memo(({ isAdult }: { isAdult: boolean }) => {
      console.log("renderMy");
      return <h1>Hello CodeSandbox</h1>;
    });
    

    And memo will help to prevent rendering of MyComponent. But parent component still render.

    https://codesandbox.io/s/interesting-lumiere-xbt4gg?file=/src/App.tsx

    If you can modify useFormData maybe useRef can help. You can store data in useRef but it doesnt trigger render.

    Something like this but in useFormData:

    onst useIsAdult = () => {
      const data = useFormData();
      const prevIsAdultRef = useRef();
      const isAdult = useMemo(() => data.age > 18, [data]);
    
      if (prevIsAdultRef.current === isAdult) {
        return prevIsAdultRef.current;
      }
    
      prevIsAdultRef.current = isAdult;
      return isAdult;
    };
    

    I dont know how useFormData works, then you can try it self with useRef.

    And if useFormData gets data very often and you cant manage this, you can use debounce or throttle to reduce number of updates

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search