title: React Hooks useState+useEffect+event gives stale state
body: <p>I'm trying to use an event emitter with React <code>useEffect</code> and <code>useState</code>, but it always gets the initial state instead of the updated state. It works if I call the event handler directly, even with a <code>setTimeout</code>. </p>
  
  <p>If I pass the value to the <code>useEffect()</code> 2nd argument, it makes it work, however this causes a resubscription to the event emitter every time the value changes (which is triggered off of keystrokes).</p>
  
  <p>What am I doing wrong? I've tried <code>useState</code>, <code>useRef</code>, <code>useReducer</code>, and <code>useCallback</code>, and couldn't get any working.</p>
  
  <p>Here's a reproduction:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>import React, { useState, useEffect } from "react";
  import { Controlled as CodeMirror } from "react-codemirror2";
  import "codemirror/lib/codemirror.css";
  import EventEmitter from "events";
  
  let ee = new EventEmitter();
  
  const initialValue = "initial value";
  
  function App(props) {
    const [value, setValue] = useState(initialValue);
  
    // Should get the latest value, both after the initial server load, and whenever the Codemirror input changes.
    const handleEvent = (msg, data) =&gt; {
      console.info("Value in event handler: ", value);
      // This line is only for demoing the problem. If we wanted to modify the DOM in this event, we would instead call some setState function and rerender in a React-friendly fashion.
      document.getElementById("result").innerHTML = value;
    };
  
    // Get value from server on component creation (mocked)
    useEffect(() =&gt; {
      setTimeout(() =&gt; {
        setValue("value from server");
      }, 1000);
    }, []);
  
    // Subscribe to events on component creation
    useEffect(() =&gt; {
      ee.on("some_event", handleEvent);
      return () =&gt; {
        ee.off(handleEvent);
      };
    }, []);
  
    return (
      &lt;React.Fragment&gt;
        &lt;CodeMirror
          value={value}
          options={{ lineNumbers: true }}
          onBeforeChange={(editor, data, newValue) =&gt; {
            setValue(newValue);
          }}
        /&gt;
        {/* Everything below is only for demoing the problem. In reality the event would come from some other source external to this component. */}
        &lt;button
          onClick={() =&gt; {
            ee.emit("some_event");
          }}
        &gt;
          EventEmitter (doesnt work)
        &lt;/button&gt;
        &lt;div id="result" /&gt;
      &lt;/React.Fragment&gt;
    );
  }
  
  export default App;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Here's a code sandbox with the same in <code>App2</code>:</p>
  
  <p><a href="https://codesandbox.io/s/ww2v80ww4l" rel="noreferrer">https://codesandbox.io/s/ww2v80ww4l</a></p>
  
  <p><code>App</code> component has 3 different implementations - EventEmitter, pubsub-js, and setTimeout. Only setTimeout works.</p>
  
  <h2>Edit</h2>
  
  <p>To clarify my goal, I simply want the value in <code>handleEvent</code> to match the Codemirror value in all cases. When any button is clicked, the current codemirror value should be displayed. Instead, the initial value is displayed.</p>