
I found myself the other day trying to build a dropdown component in Reason that on outside click would close the menu if opened.
The only example I could find of this was in Reason fromGlennSL and it was a bit outdated since it was written in 2018, before hooks were fully implemented into reason. The code below is an updated version using hooks, it requires thebs-dependency
ofbs-webapi
so make sure to add that before you use it.
[@genType"OnClickOutside"][@react.component]letmake=(~onOutsideClick,~children)=>{letoutsideContainer=React.useRef(Js.Nullable.null);React.useEffect0(()=>{openWebapi.Dom;letonClick=e=>{lettarget=MouseEvent.target(e);letoutsideDiv=Belt.Option.getExn(Js.Nullable.toOption(React.Ref.current(outsideContainer)),);lettargetElement=EventTarget.unsafeAsElement(target);if(!Element.contains(targetElement,outsideDiv)){onOutsideClick();};};Document.addClickEventListener(onClick,document);Some(()=>Document.removeClickEventListener(onClick,document));});<divref={outsideContainer->ReactDOMRe.Ref.domRef}>children</div>;};
The logic breakdown:
- wrap the component in a div with a ref
- add an event listener for onClick
- in that onClick function check whether the element clicked contains the div with the ref, if it does then the click is inside the div. If not then the click is outside of the div.
- If the click is outside of the container then do whatever you need it to(in this case above, close the menu)
In the use case above we wrap our select component in theOnClickOutside
and onOutsideClick close the dropdown menu container like so;
let(visible,setVisibility)=React.useState(()=>false);<OnClickOutsideonOutsideClick={_e=>setVisibility(_=>false)}><Select.Buttontoggled=visibleonClick={_e=>setVisibility(_=>!visible)}/><Select.Listnametoggled=visible/></OnClickOutside>
If you enjoy writing Reason and would like to write more of it, you're in luck. Draftbit ishiring, we're a no-code tool that let's users build cross-platform apps and we're built mostly in reason! Check us out!
UPDATE:
I was experiencing some state issues with the trigger so I added an extra useEffect to fix this.
[@genType "OverlayTrigger"][@react.component]let make = (~onClick, ~children) => { let outsideContainer = React.useRef(Js.Nullable.null); open Webapi.Dom; let onClickHandler = event => { let target = MouseEvent.target(event); let outsideDiv = Belt.Option.getExn( Js.Nullable.toOption(React.Ref.current(outsideContainer)), ); let targetElement = EventTarget.unsafeAsElement(target); if (!Element.contains(targetElement, outsideDiv)) { onClick(); }; }; React.useEffect2( () => { Document.addMouseDownEventListener(onClickHandler, document); Some( () => Document.removeMouseDownEventListener(onClickHandler, document), ); }, (onClick, React.Ref.current(outsideContainer)), ); <div className="cursor-pointer w-full" ref={outsideContainer->ReactDOMRe.Ref.domRef}> children </div>;};
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse