0

Newbie to react here, what is the right way to manage state from synthetic events using hooks and update the state from event handlers, i have gone through the official react documentation and could not find the specific information.

This is what i have tried so far.

Step 1: Declare the hook variable using which we will update state

const [state, setState] = React.useState(0);

Step 2: Declare the function to handle synthetic event & update the state

const handleCopy = (e) => {
   setState(state+1);
   e.preventDefault();
};

Step 3: Add event listener and pass event to the function

document.addEventListener('copy', (e) => handleCopy(e));

Step 4: Pass the state to the component

<Component value={state}/>

There are few problem i face here though

  1. The state update is not reflecting in the component. But it does seem to be updating the state since the console.log seems to reflect the state correctly.
  2. There are too many event-listeners getting registered over time.

To prevent too many event-listeners i wrapped the addEventListener with a condition to register only once

const [syntheticEventRegistered, setSyntheticEventRegistered] = React.useState(false);

if(!syntheticEventRegistered)
{
    document.addEventListener('copy', (e) => handleCopy(e));
    setSyntheticEventRegistered(true);
}

This did resolve too many event listeners, but the handleCopy function now reflects only default state. i.e state = 0 instead of latest state nor does this update Component value.

Ram Kumar
  • 828
  • 2
  • 10
  • 27

1 Answers1

1

Registering an event listener is recommended in the componentDidMount lifecycle. You can use the useEffect hook in a functional component instead. With useEffect you can subscribe and unsuscribe to en event (in a return function). This way you can prevent multiple registration. I created an example.

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";
const MyFunctionalComponent = () => {
  let [state, setState] = useState(0);

  useEffect(() => {
    const handleCopy = e => {
      setState(state + 1);
      e.preventDefault();
    };

    document.addEventListener("copy", handleCopy);
    // Specify how to clean up after this effect:
    return () => {
      document.removeEventListener("copy", handleCopy);
    };
  }, [state]);

  return <div>{state}</div>;
};

function App() {
  return (
    <div className="App">
      <MyFunctionalComponent />
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

You can check out here: https://codesandbox.io/s/little-rgb-dfre7

It works for me. If you select something from the right side and press ctrl+c the counter increases.

Peter Ambruzs
  • 7,763
  • 3
  • 30
  • 36
  • Thanks for your input. I removed the function in "return function () =>" and it pretty much works as expected for updating state, but doesn't re-render the component with the new state. – Ram Kumar Sep 14 '19 at 10:16
  • I do not follow your render part yet. Try something simple for example `
    {state}
    ` It should work.
    – Peter Ambruzs Sep 14 '19 at 10:28
  • Even the simple div is not updating after updating the state – Ram Kumar Sep 14 '19 at 10:32
  • I still not know what is wrong with your code. Maybe you use Component, that you supposed to extend a new component from. Anyway I updated my answer with a working example. – Peter Ambruzs Sep 14 '19 at 13:12
  • My bad, it was an issue with the props to the component i was using, appreciate your help, accepted this as answer. – Ram Kumar Sep 14 '19 at 13:29