title: How to deal with stale state values inside of a useEffect closure?
body: <p>The following example is of a Timer component that has a button (to start the timer), and two  tags that display the number of elapsed seconds, and the number of elapsed seconds times 2.</p>
  <p>However, it does not work (<a href="https://codesandbox.io/s/vibrant-panini-wj1h6?file=/src/App.js" rel="nofollow noreferrer">CodeSandbox Demo)</a></p>
  <p><strong>The Code</strong></p>
  <pre><code>import React, { useState, useEffect } from &quot;react&quot;;
  
  const Timer = () =&gt; {
    const [doubleSeconds, setDoubleSeconds] = useState(0);
    const [seconds, setSeconds] = useState(0);
    const [isActive, setIsActive] = useState(false);
  
    useEffect(() =&gt; {
      let interval = null;
      if (isActive) {
        interval = setInterval(() =&gt; {
          console.log(&quot;Creating Interval&quot;);
          setSeconds((prev) =&gt; prev + 1);
          setDoubleSeconds(seconds * 2);
        }, 1000);
      } else {
        clearInterval(interval);
      }
      return () =&gt; {
        console.log(&quot;Destroying Interval&quot;);
        clearInterval(interval);
      };
    }, [isActive]);
  
    return (
      &lt;div className=&quot;app&quot;&gt;
        &lt;button onClick={() =&gt; setIsActive((prev) =&gt; !prev)} type=&quot;button&quot;&gt;
          {isActive ? &quot;Pause Timer&quot; : &quot;Play Timer&quot;}
        &lt;/button&gt;
        &lt;h3&gt;Seconds: {seconds}&lt;/h3&gt;
        &lt;h3&gt;Seconds x2: {doubleSeconds}&lt;/h3&gt;
      &lt;/div&gt;
    );
  };
  
  export { Timer as default };
  </code></pre>
  <p><strong>The Problem</strong></p>
  <p>Inside the useEffect call, the &quot;seconds&quot; value will always be equal to the its value when the useEffect block was last rendered (when isActive last changed). This will result in the <code>setDoubleSeconds(seconds * 2)</code> statement to fail. The React Hooks ESLint plugin gives me a warning regarding this problem that reads:</p>
  <blockquote>
  <p><em>React Hook useEffect has a missing dependency: 'seconds'. Either include it or remove the dependency array. You can also replace
  multiple useState variables with useReducer if 'setDoubleSeconds'
  needs the current value of 'seconds'.
  (react-hooks/exhaustive-deps)eslint</em></p>
  </blockquote>
  <p>And correctly so, adding &quot;seconds&quot; to the dependency array (and changing <code>setDoubleSeconds(seconds * 2)</code> to <code>setDoubleSeconds((seconds + 1) * )</code> will render the correct results. However, this has a nasty side effect of causing the interval to be created and destroyed on every render (the <code>console.log(&quot;Destroying Interval&quot;)</code> fires on every render).</p>
  <p>So now I am looking at the other recommendation from the ESLint warning <em>&quot;You can also replace multiple useState variables with useReducer if 'setDoubleSeconds' needs the current value of 'seconds'&quot;</em>.</p>
  <p>I do not understand this recommendation. If I create a reducer and use it like so:</p>
  <pre><code>import React, { useState, useEffect, useReducer } from &quot;react&quot;;
  
  const reducer = (state, action) =&gt; {
      switch (action.type) {
          case &quot;SET&quot;: {
              return action.seconds;
          }
          default: {
              return state;
          }
      }
  };
  
  const Timer = () =&gt; {
      const [doubleSeconds, dispatch] = useReducer(reducer, 0);
      const [seconds, setSeconds] = useState(0);
      const [isActive, setIsActive] = useState(false);
  
      useEffect(() =&gt; {
          let interval = null;
          if (isActive) {
              interval = setInterval(() =&gt; {
                  console.log(&quot;Creating Interval&quot;);
                  setSeconds((prev) =&gt; prev + 1);
                  dispatch({ type: &quot;SET&quot;, seconds });
              }, 1000);
          } else {
              clearInterval(interval);
          }
          return () =&gt; {
              console.log(&quot;Destroying Interval&quot;);
              clearInterval(interval);
          };
      }, [isActive]);
  
      return (
          &lt;div className=&quot;app&quot;&gt;
              &lt;button onClick={() =&gt; setIsActive((prev) =&gt; !prev)} type=&quot;button&quot;&gt;
                  {isActive ? &quot;Pause Timer&quot; : &quot;Play Timer&quot;}
              &lt;/button&gt;
              &lt;h3&gt;Seconds: {seconds}&lt;/h3&gt;
              &lt;h3&gt;Seconds x2: {doubleSeconds}&lt;/h3&gt;
          &lt;/div&gt;
      );
  };
  
  export { Timer as default };
  </code></pre>
  <p>The problem of stale values will still exist (<a href="https://codesandbox.io/s/zealous-water-72ck7?file=/src/App.js" rel="nofollow noreferrer">CodeSandbox Demo (using Reducers)</a>).</p>
  <p><strong>The Question(s)</strong></p>
  <p>So what is the recommendation for this scenario? Do I take the performance hit and simply add &quot;seconds&quot; to the dependency array? Do I create another useEffect block that depends on &quot;seconds&quot; and call &quot;setDoubleSeconds()&quot; in there? Do I merge &quot;seconds&quot; and &quot;doubleSeconds&quot; into a single state object? Do I use refs?</p>
  <p>Also, you might be thinking &quot;Why don't you simply change <code>&lt;h3&gt;Seconds x2: {doubleSeconds}&lt;/h3&gt;</code>&quot; to <code>&lt;h3&gt;Seconds x2: {seconds * 2}&lt;/h3&gt;</code> and remove the 'doubleSeconds' state?&quot;. In my real application doubleSeconds is passed to a Child component and I do not want the Child component to know how seconds is mapped to doubleSeconds as it makes the Child less re-usable.</p>
  <p>Thanks!</p>