title: React state change best practices: useEffect vs callback
body: <p>I have a question regarding best practices when using useEffect and when to use it to handle state changes.</p>
  <p>For example let's say I have a button that will send an email on the 5th click (pseudo code as I didn't really test to see if it works). What's better according to React best practices.</p>
  <p>Here I am using <strong>useEffect</strong> to handle the count state change:</p>
  <pre><code>import React, { useState, useEffect } from 'react'
  export default () =&gt; {
      const [count, setCount] = useState(0)
      useEffect(() =&gt; {
          if (count === 5) {
              sendEmail()
              setCount(0)
          }
      }, [count])
  
      return (
          &lt;div&gt;
              &lt;input
                  type=&quot;button&quot;
                  onClick={() =&gt; {
                      setCount((count) =&gt; count + 1)
                  }}
              &gt;&lt;/input&gt;
          &lt;/div&gt;
      )    
  }
  </code></pre>
  <p>Here is the same functionality with a <strong>callback</strong>:</p>
  <pre><code>import React, { useState } from 'react'
  export default () =&gt; {
      const [count, setCount] = useState(0)
     
      return (
          &lt;div&gt;
              &lt;input
                  type=&quot;button&quot;
                  onClick={() =&gt; {
                      if(count + 1 === 5) {
                          sendEmail()
                          setCount(0)
                          return
                      }
                      setCount(count=&gt;count+1)
                  }}
              &gt;&lt;/input&gt;
          &lt;/div&gt;
      )    
  }
  </code></pre>
  <p>The argument for using useEffect, at least as I see it, is to react to state changes. However if I want to check other state in that useEffect block or other dependencies, then I have to add it to the dependency array and it's in danger of re-running even though I'm only interested in running it in response to the count changing (one piece of state). In this example it isn't too egregious but in more advanced situations it seems that you have to add a bunch of conditionals in the useEffect block if you only want it to react to one piece of state change. if(x,y,z) {run count logic}</p>
  <p>The argument for the callback is that it's readable and will only be called on the button being pressed however it is more imperative than declarative and not as REACTive as useEffect. Furthermore with the setCount being async it might create a race condition (not in this case but in more advanced cases).</p>
  <p>Also I can use useEffect to run code when a component has first rendered but if it has logic that touches dependencies I have to add them to the dependency array according to my linter and Dan Abramov but then I run the chance of running it multiple times unless I add a bunch of conditionals in the body of useEffect (as I mentioned above). In short I may want to run the useEffect on one piece of state change and not when any of the dependencies change.</p>
  <p>I hope that makes sense.</p>
  <p>An example of the useEffect issue with conditionals is if I want to send the email if some localState variable === &quot;whatever&quot;. Once I add the localState variable into the dep array, then it will run whenever it changes even if I only want to run it when specific state (count) changes. The only way around this I see is adding logic on the useEffect to exit early but it starts to get complicated and error prone.</p>