Why Native DOM event always reset React Component state

I know that the Synthetic Event, instead of the native dom event, should be the way to deal with interaction in React App, but I wonder why the behavior is so bizarre. When the React Event works well, and the dom event always reset the state to initial state.

import React, { useState, useEffect } from "react";
import "./style.css";

export default function App() {
  const [num, setNum] = useState(0);
  function clickMe() {
    setNum(num + 1);
  }
  useEffect(() => {
    document.getElementById("app_btn").addEventListener("click", clickMe);
    return () =>
      document.getElementById("app_btn").removeEventListener("click", clickMe);
  }, []);
  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <div>App {num}</div>
      <button id="app_btn">DOM click</button> <br />
      <button onClick={clickMe}>React Click</button>
    </div>
  );
}

The full project can be opened here https://stackblitz.com/edit/react-wqrmcu?file=src/App.js

3 thoughts on “Why Native DOM event always reset React Component state”

  1. That’s happening because your native event handler still closes over the value of num that was available after first render (that value is 0). You need to add and remove native event listener at each state update for this to work like so :-

      useEffect(() => {
        document.getElementById("app_btn").addEventListener("click", clickMe);
        return () =>
          document.getElementById("app_btn").removeEventListener("click", clickMe);
      }, [num]);
    

    Another approach could be using a callback inside the state updator function as @Mahdi pointed out. This will allow you to get new state on the basis of previous state. But still the value of num being accessible to native event listener is 0. You can verify that by doing a console.log(num) inside clickMe.

    Reply

Leave a Comment