title: Best Practice: useEffect deps with setState or dispatch, via useContext or custom hook
body: <p>I'm working on wrapping my head around all the best practices for dealing with the dependency array in <code>useEffect()</code>. First, some guidelines (from <a href="https://overreacted.io/a-complete-guide-to-useeffect/" rel="nofollow noreferrer">this blog post</a> &amp; elsewhere):</p>
  <ol>
  <li>If you're calling a function in useEffect that's <em>only</em> used within useEffect, just define it within <code>useEffect()</code>. Not needed in deps.</li>
  <li>If you're calling a function in useEffect that's used both within &amp; without useEffect, but it doesn't need to access anything from the scope of the component, hoist it outside of the component. Not needed in deps.</li>
  <li>If you're calling function in useEffect that's a dispatcher returned by <code>useReducer()</code> or a setter returned by <code>useState()</code>, you don't need to include it in dependencies (it doesn't hurt, but you don't have to).</li>
  <li>Otherwise, wrap your function in useCallback &amp; add it to useEffect's dependency array.</li>
  </ol>
  <p>Is all of this correct?  If so, my question:</p>
  <ol start="5">
  <li>If you use useContext or a custom hook to get a setter/dispatcher that was created <em>elsewhere</em> by useState/useReducer, and call it in useEffect, the linter will warn you to add it to the dependency array.  I assume that this is because the linter just doesn't know that the function came from useState/useReducer, so it has to err on the side of caution &amp; warn you. Thus, it seems like best practice is to just chuck [dispatch] or [setSomething] in the deps array: just like in point #3 above, it doesn't hurt (it'll still behave like an empty array), and doing so will satisfy the linter. This seems to be the answer <a href="https://stackoverflow.com/a/66604150/136785">here</a>.  However, I have seen multiple resources that say you should actually still wrap it in useCallback (i.e. <a href="https://stackoverflow.com/a/58624276/136785">here</a> - I realize this also involves a different issue, but I've definitely seen stableDispatch in quite a few places):</li>
  </ol>
  <pre class="lang-js prettyprint-override"><code>const [state, dispatch] = useContext(MyContext);
  const stableDispatch = useCallback(dispatch, []);
  
  useEffect(() =&gt; {
    stableDispatch(foo)
  },[stableDispatch]);
  </code></pre>
  <p>However, that doesn't really seem to do anything. Rather than the linter warning you about deps missing in useEffect, now it just warns you about deps missing in useCallback.  So to be more concise, my questions are:</p>
  <ul>
  <li>Are #1-4 accurate?</li>
  <li>In the case of #5, what is the best practice (that also resolves the linter warning, but also doesn't result in running the effect unnecessarily)? Just put 'dispatch' directly in useEffect's deps, or use useCallback, or is there a different approach?</li>
  </ul>
  <p>Any clarifications are greatly appreciated.</p>