skip to Main Content

I want to display a list of coordinates that I got from click event on DIV. I want the listener works only when a button was clicked.

export function AppRestoration() {

  const [active, setActive] = useState(false)
  const [coordinates, setCoordinates] = useState<any[]>([])

  const handleClick = (event) => {

    // I need to get access to coordinates here (to filter them), but it is always an empty array
    console.log(coordinates)
    setCoorrdinates(prevCoordinates => [...prevCoordinates, event.clientX]);
  }

  React.useEffect(() => {
    if (active) {

      const handleGlobal = (event) => {
        handleClick(event)
      }
      document.getElementById("alex")?.addEventListener('click', handleGlobal)
      return () => document.getElementById("alex")?.removeEventListener('click', handleGlobal)
    }
  }, [active])

  return (
    <div>
      <div id="alex" style={{ width: '200px', height: '200px', backgroundColor:'red' }}>ici</div>
      <button onClick={() =>setActive(prev => !prev)}>{active ? 'clicked' : 'not'}</button>
      <SecondComponent
        secondCParam={coordinates} />
    </div>
  )
}

enter image description here

The problem is : in handleClick(event) I can not filter "coordinates", it is always an empty array. To refresh it, I have to click on the button, so handleClick one more time. How to avoid it?

2

Answers


  1. You should avoid using native JS event listeners in React. Here is how you can refactor your code to achieve the same result with a React approach:

    export function AppRestoration() {
    
      const [state, setState] = useState(false);
      const [coordinates, setClickCoord] = useState<any[]>([]);
    
      return (
        <div>
          <div id="alex" onClick={() => {
       if (state) setClickCoord(prevCoordinates => [...prevCoordinates, event.clientX]);
    }} style={{ width: '200px', height: '200px', backgroundColor:'red' }}>ici</div>
          <button onClick={() => setState(!state)}>{state ? 'clicked' : 'not'}</button>
          <SecondComponent
            secondCParam={coordinates} />
        </div>
      )
    }
    

    However, if you want to experiment with event listeners and use this pattern anyway, you could refactor handleClick like so:

    const handleClick = (event) => {
        // I need to get access to coordinates here (to filter them), but it is always an empty array
        setClickCoord(prevCoordinates => {
          console.log(prevCoordinates);
          return [...prevCoordinates, event.clientX]);
        });
      }
    

    Since the list is updating but the console.log shows nothing, the issue might be that your console.log only displays the initial value of coordinates ([]).

    I would strongly advise you to not use event listeners in React and to use refs when you need to interact with DOM elements (instead of using getElementById or querySelector).

    Login or Signup to reply.
  2. Why not add the click handler to the element, and do it the React way?

    <div
      style={{ width: "200px", height: "200px", backgroundColor: "red" }}
      onClick={handleClick}
    >
    
    const handleClick = useCallback(({ clientX }) => {
      setCoordinates((prevCoordinates) =>
        !prevCoordinates.includes(clientX)
          ? [...prevCoordinates, clientX]
          : prevCoordinates
      );
    }, [setCoordinates]);
    

    Also, a state called state is ambiguous. You should rename it to a more appropriate name. For the time being, I named it active, since the button toggles.

    const { useCallback, useEffect, useState } = React;
    
    const AppRestoration = () => {
      const [active, setActive] = useState(false);
      const [coordinates, setCoordinates] = useState([]);
    
      const handleClick = useCallback((event) => {
          setCoordinates((prevCoordinates) =>
            !prevCoordinates.includes(event.clientX)
              ? [...prevCoordinates, event.clientX]
              : prevCoordinates
          );
        }, [setCoordinates]);
    
      useEffect(() => {
        console.log("Coordinates:", ...coordinates); // When they update
      }, [coordinates]);
    
      return (
        <div>
          <div
            style={{ width: "200px", height: "200px", backgroundColor: "red" }}
            onClick={handleClick}
          >
            ici
          </div>
          <button onClick={() => setActive((prev) => !prev)}>
            {active ? "clicked" : "not"}
          </button>
          <SecondComponent secondCParam={coordinates} />
        </div>
      );
    };
    
    const SecondComponent = ({ secondCParam }) => {
      return (
        <ul>
          {secondCParam.map((coord) => (
            <li key={crypto.randomUUID()}>{coord}</li>
          ))}
        </ul>
      );
    };
    
    ReactDOM.createRoot(document.getElementById("root")).render(<AppRestoration />);
    .as-console-wrapper { max-height: 4rem !important; }
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search