Search code examples
javascriptreactjsdomreact-state-management

Detect click outside a component react way NOT dom access


I am implementing a crop feature with React. When the user right clicks, a context menu appears. In the context menu, a user can select crop item. When a user selects crop, cropping gets activated. Now, I want to close the cropping state when the user clicks outside of that division. How do I have to do react way without DOM manipulation? Or should I have to use DOM manipulations such as add EventListener & removeListener? What are the cons of using DOM access here? Please let me know this thoroughly since this is going to be implemented in many of the features I am going to develop. For example, the context menu I was talking about, or the dropdown menu.

Here is an example for the reference - Sandbox. Now, when the user clicks outside of ReactCrop component, I want to close the crop through react way.

I don't want to go ahead with document.addEventListener or removeListener.


Solution

  • If you really, really don't want to attach dom events to your components, you can add a 'dismisser' component behind the Crop component and handle the reset there.

    const Dismisser = ({ onClick }) => <div onClick={onClick} style={{
      position: 'fixed',
      top: 0, left: 0, right: 0, bottom: 0,
      zIndex: 100,
    }} />
    

    In your component:

    {src && (
      <React.Fragment>
      <div style={{position: 'relative', zIndex: 200}}>
        <ReactCrop
          src={src}
          crop={crop}
          ...
        />
      </div>
      <Dismisser onClick={this.resetCrop} />
      </React.Fragment>
    )}
    

    Codesandbox

    I'd say that's a bit more 'React-y' since the responsiblity of reseting the crop is delegated to another component. However it'll give you trouble when your Crop component is behind other Dom element, z-index wise. You can get over it by using portal, but it'll start to get complex.

    I think attaching DOM event is a valid solution here (as long as you remember to remove them). It's simpler, plus it's not like you're directly manipulating a dom node using data outside of React's flow, which most of the time is the real bad case.