the only time I find it to be required is when passing in callbacks to custom hooks with props that may change and you'll notice immediately because the callback will continuously run
If you're using React.useCallback to avoid triggering further hooks down the line, then you're using it wrong. Since React.useCallback is ostensibly just React.useMemo wrapped around a function, this note in the documentation is just as applicable:
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.
I was following a pattern similar to react-table, where I'm destructuring a function off of a previous hook and passing it to another hook as a callback. This allows the 2nd hook to update state in the first, which causes it to infinite loop if the function isn't wrapped. I think this pattern is more complicated than it sounds, but really helps to remove business logic from a component
If we have a component
<ProfilePage id={id} onLoad={handleOnLoad} />
and inside profile page we use an effect to load from id and call handleOnLoad with the loaded profile data, then we need to put handleOnLoad in the useEffect dependencies. So we have to pass a callback wrapped in useCallback else the effect would fire every time ProfilePage rerenders.