Search code examples
reactjsreact-hooksuse-effectuse-state

Function not able to get the latest state value


I am new to react state hook. Here is the sample code:

import React, { useState, useEffect } from 'react';

export function App(props) {
  const [c, setC] = useState(0);
  const [b1, setB1] = useState(null);

  useEffect(() => {
    setC(1);
    let b1 = document.getElementById("b1");
    b1.addEventListener("click", f1);
    setB1(b1);
  }, []);

  const f1 = () => {
    console.log(c);
  }

  return (
    <div className='App'>
      <h1>Hello React.</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button id="b1" />
    </div>
  );
}

// Log to console
console.log('Hello console')

If I click the button. The console always output 0 instead of 1 where i set the value in useEffect.

Please let me know what's wrong here. Looks like when I add eventlistener on the button, it remembers the C value instead of reading the C value from state every time.


Solution

  • At the time that the click listener is attached, the render that the state value closes over is that when the useEffect callback was declared - which is when c was still the prior value from back then. While you could fix it by logging c + 1, a much better approach would be to avoid vanilla DOM methods entirely in React, and do it the React way instead, with a JSX onClick prop on the button.

    const App = () => {
      const [c, setC] = React.useState(0);
      React.useEffect(() => {
        setC(1);
      }, []);
      const f1 = () => {
        console.log(c);
      }
      return (
        <div className='App'>
          <h1>Hello React.</h1>
          <h2>Start editing to see some magic happen!</h2>
          <button onClick={f1}>click</button>
        </div>
      );
    }
    
    ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div class='react'></div>