react: eslint-plugin-react-hooks useEffect autofix of adding function names causes a lot of infinite loops

I’ve read “My function value is constant” from https://github.com/facebook/react/issues/14920#issuecomment-471070149.

There is a problem on the opposite spectrum of this, which is where you get infinite loops (a function value always changes). We catch that in the lint rule now when possible (in the same component) and suggest a fix. But it’s tricky if you pass something several levels down.

If you autofix the useEffect, would it also be possible to autofix any functions added by wrapping them in a useCallback at the same time?

This would greatly improve the user experience.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 20

Most upvoted comments

A much better fix is usually to either move a function either outside of the component (if it doesn’t use props or state), or move it inside the effect (if it does). useCallback is the escape hatch for when you can’t or don’t want to do either. So I don’t think we’d want to autofix it to useCallback every time.

We could offer an option that disables the autofix when we detect function deps.

Regarding the autofix, I hope https://github.com/eslint/rfcs/pull/30 will let us solve the problem without compromising on IDE suggestions.

Yeah, i’m using vscode if that helps. The auto format/fixing is adding the setters and vars in the array and that creates an infinite loop. I added //eslint-disable-next-line and now it works but that feels like a hack lol

I have a problem when --fix is enabled on this line. It changes the deps to:

  const ref = useRef<HTMLElement>(null);
  const [shortcuts, setShortcuts] = useState<ShortcutAction[]>();

  useLayoutEffect(() => {
    const trapper = scoped && ref.current ? new mousetrap(ref.current) : mousetrap;

    const map = mapKey ? (shortcutMap[mapKey] as Shortcut) : (shortcutMap as Shortcut);

    const shortcutActions = buildShortcuts(map);

    shortcutActions.forEach((shortcut) => {
      trapper.bind(shortcut.keys, (e: ExtendedKeyboardEvent) => {
        e.preventDefault();
        e.stopPropagation();

        handler(shortcut.action, e);
      });

      shortcut.trapper = trapper;
    });

    setShortcuts(shortcutActions);

    return () => {
      if (!shortcuts) {
        return;
      }

      shortcuts.forEach((shortcut) => {
        if (shortcut.trapper) {
          shortcut.trapper.unbind(shortcut.keys);
        }
      });
    };
  }, [handler, mapKey, scoped, shortcutMap, shortcuts]);

Adding the shortcuts dep will cause an infinite loop as setShortcuts is called which will cause a rerender.