Home > Blockchain >  How do I add this event listener that removes a popup on click?
How do I add this event listener that removes a popup on click?

Time:01-26

I have a popup that can appear on my screen, whether it appears or not is controlled by a state. When the popup appears, I would like to be able to click anywhere on the screen to make it disappear. I was thinking of the following:

function App() {
  const [isPopup, setPopup] = useState(false);
  
  useEffect(() => {
    if (!isPopup) { // if the popup just turned off, do nothing
      return;
    }

    const handleClick = () => {
       setPopup(false);
    };
      
    window.addEventListener('click', handleClick);

    return () => window.removeEventListener('click', handleClick);
    
  }, [isPopup]);

This should do the following:

When the popup state changes, run the useEffect. If the popup state is true (meaning the popup just turned on), then create a handler that turns it off, and add that handler to respond to a mouse click. When the component un-mounts, remove it.

However, I believe this may create a few issues:

  1. If we add event listeners every time the popup state is true, that seems bad? I think it won't actually add duplicate ones, but it suggests inefficient code design.
  2. Similarly, removing event listeners only on unmount seems bad, as opposed to when we click to remove the popup.
  3. Running the useEffect each time we change the popup is inefficient, as half the time we are changing the popup to false, and we just return immediately.

How can I solve for the above three issues?

CodePudding user response:

You don't need to use useEffect at all.

function App() {
  const [isPopup, setPopup] = useState(false);

function handleClick(e){
if(e.target.id === "popup" && !isPopup) setPopup(true)
if(e.target.id !== "popup" && isPopup) setPopup(false)
}

return <main id="main" onClick={handleClick}>
{isPopup && <div id="popup" onClick={handleClick}></div>}
///Your JSX here
</main>
  
}

One function can handle both cases in two lines. Just make it sure that tag occupies all the screen available. And use UseEffect only for actual effects.

CodePudding user response:

  1. instead of using useEffect you may use useRef hook to access dom objects without re-rendering the application which is more efficient.

const eventListenerAdded = useRef(false);

useEffect(() => { if (!isPopup || eventListenerAdded.current) { // if the popup just turned off, or the event listener has already been added, do nothing return; }

const handleClick = () => {
   setPopup(false);
};
  
window.addEventListener('click', handleClick);
eventListenerAdded.current = true;

return () => window.removeEventListener('click', handleClick);

}, [isPopup]);

const eventListenerAdded = useRef(false);

useEffect(() => {
    if (!isPopup || eventListenerAdded.current) { // if the popup just turned off, or the event listener has already been added, do nothing
      return;
    }

    const handleClick = () => {
       setPopup(false);
    };
      
    window.addEventListener('click', handleClick);
    eventListenerAdded.current = true;

    return () => window.removeEventListener('click', handleClick);
    
  }, [isPopup]);

  1. To remove the event listener when the popup is closed, you can add a cleanup function to the useEffect that removes the event listener when the popup is closed.

return () => {
  window.removeEventListener('click', handleClick);
  eventListenerAdded.current = false;
}

3.To prevent the useEffect from running when the popup is turned off, you can add a dependency to the useEffect that only causes it to run when the popup is turned on.

useEffect(() => {

  
}, [isPopup, eventListenerAdded]);

CodePudding user response:

You don't need a useEffect for this example. I share an example for you. You can copy paste and test it.

import * as React from 'react';

export default function App() {
  const [show, setShow] = React.useState(false);

  return (
    <main
      style={{ height: '100vh', width: '100vh', position: 'relative' }}
      onClick={() => {
        setShow(false);
      }}
    >
      <button
        onClick={(e) => {
          e.stopPropagation();
          setShow(true);
        }}
      >
        Show
      </button>

      {show && (
        <div
          style={{
            top: 0,
            left: 0,
            position: 'fixed',
            opacity: '.8',
            backgroundColor: '#dedede',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            width: '100vh',
            height: '100vh',
          }}
        >
          <div
            style={{
              width: 200,
              height: 200,
              backgroundColor: 'blue',
            }}
            onClick={(e) => {
              e.stopPropagation();
            }}
          >
           Modal active area. Model close event is should not be trigger in this area.
          </div>
        </div>
      )}
    </main>
  );
}


  • Related