skip to Main Content

VSCode doesn’t mark this as invalid, despite the hooks being conditionally called:

function useHook() {
  return 1;
}

function useHook2() {
  const two = useMemo(() => 2, []);
  return two;
}

const hookDictionary = {
  useHook,
  useHook2
}

export function HookUser(index) { 
  let func = hookDictionary[index];
  func();
  return null;
}

Is this intentional, and considered valid?
Is it being hidden by the use of hookDictionary?

My assumption is that this is the case, since it’s functionally the same as the following which does throw an error:

export function HookUser(index: string) {
  if (index === 'useHook') {
    useHook();
  } else if (index === 'useHook2') {
    useHook2();
  }

  return null;
}

2

Answers


  1. Consider this code :

    export function Test() {
        const [index, setIndex] = useState<'useHook' | 'useHook2'>('useHook2');
        HookUser(index);
        return (
            <div>
                <Button onClick={() => setIndex((prev) => (prev === 'useHook' ? 'useHook2' : 'useHook'))} />
            </div>
        );
    }
    

    Where HookUser is the same as yours. The moment I click on the button it will crash.

    In this case your IDE seems to assume that the index will not change (since there is no condition inside of HookUser). Because in this case the condition is coming from other component, and the IDE doesn’t have the context to check for error (since HookUser isn’t considered as a hook)

    You can export those hooks separately so there will never be an error like that.

    Or you can also do

    export function useSuperHook() {
        ...someContext
    
        function useHook() {
            return 1;
        }
        function useHook2() {
            const two = useMemo(() => 2, []);
            return two;
        }
        
        return { useHook, useHook2 };
    }
    
    // in some component
    
    const { useHook } = useSuperHook();
    
    Login or Signup to reply.
  2. Is this intentional, and considered valid?

    Intentional, no. Considered valid, also no.

    Is it being hidden by the use of hookDictionary?

    Basically, yes. ESLint doesn’t do a deep-dive of your code to ensure it compiles and doesn’t create runtime errors. React hooks are really just regular old Javascript functions that have some special rules applied to them, namely the when and where they can be called. These functions are identified by their "use"-prefix.

    • The linter checking React hooks rules checks the first code:

      function useHook() {
        return 1;
      }
      

      Nothing fishy here, it checks out.

    • Then the second:

      function useHook2() {
        const two = useMemo(() => 2, []);
        return two;
      }
      

      Ok, the useMemo hook is called. It’s unconditionally called from within another React hook, so it also checks out.

    • Then the third and fourth code:

      const hookDictionary = {
        useHook,
        useHook2
      }
      
      export function HookUser(index) { 
        let func = hookDictionary[index];
        func();
        return null;
      }
      

      No hooks, i.e. no "use"-prefixed functions, called here, so these too also check out.

    The linter doesn’t check references to see if the func function is really a React hook function in disguise. It wouldn’t be until the code is running and the index value changes and then a different hook was called from the previous render cycle, and an error is thrown indicating such. Even if you renamed func to useFunc, I still don’t think the Linter would be able to pick up on the switching abstracted behind hookDictionary[index].

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