‫Next up, let's write some code
‫to detect a click that happens outside the modal window,
‫and so when that happens, we then want to close the window.
‫So basically, what I mean is that when this is open,
‫and then when we click out here,
‫that modal window should close,
‫which is a pretty common pattern
‫in websites and applications.
‫So of course, right now, as I click here,
‫that doesn't happen,
‫and so what we want to do now, again,
‫is to detect whenever a click happens
‫outside of the modal window or one of its children,
‫so basically, in this case, out here.
‫All right, so let's come here to our window,
‫and then add some global event listeners for a click event.
‫So now, we will basically go back, so to say,
‫to some more primitive DOM manipulation.
‫So we need to do that inside a useEffect, right?
‫And so let's specify our effect function,
‫and as always, our dependency array.
‫So again, here, we will now attach right to the document
‫an event listener for any click events,
‫and then here, we will have a handleClick callback function,
‫and it needs to be in this external function,
‫because we also need to remove this.
‫So handleClick, which will receive the click event,
‫and so as I was saying earlier,
‫we also now need to remove this event listener
‫as the component unmounts,
‫and so for that, remember, we need to return
‫a callback function,
‫so we return document.removeEventListener,
‫.click, and then that same handleClick.
‫Now, here, apparently, we didn't include useEffect,
‫or actually, we did, if we read the error message here,
‫and so thankfully for us, we have this ESLint rule,
‫which tells us that the React Hook, useEffect
‫is called conditionally.
‫So remember how we learned earlier, way back,
‫that we are not allowed to do this,
‫but if I didn't have this ESLint rule,
‫I might actually have missed this,
‫so then that would have created those kind of bugs
‫that we talked about earlier,
‫but now, that works, and so now,
‫let's really think about this.
‫So let's open up the window again,
‫and as we just said earlier,
‫we want something to happen whenever we click outside here.
‫So basically, whenever the click event
‫that we are handling here happens outside
‫of this element right here, so outside of this StyledModal,
‫so let's then manually select this element using a ref.
‫So const ref will come from the useRef hook just like this,
‫and now, we can add that here with the ref prop,
‫and then we can use this selected element
‫right here in our handler.
‫So first of all, we need to check if that ref even exists,
‫so for that, we can check ref.current,
‫which is basically where the DOM node
‫that references this element here will be stored.
‫So if that exists, and now, comes basically the heart,
‫or the solution of this whole thing.
‫So I will just write it, and then I will explain it.
‫So if the ref.current, so again,
‫that's this window right there,
‫so if that does not contain the element that was clicked,
‫so e.target, then in that case, close the modal.
‫Now, here, we are missing something,
‫which is probably that ref,
‫or actually, refs are stable,
‫but the close function, we do need.
‫So let's recap here.
‫Maybe let's even add some console.log here first,
‫so let's say, click outside,
‫and so let's again analyze.
‫So remember that ref.current is now this element right here,
‫so this white modal window.
‫So it's a DOM element right now,
‫and on DOM elements, we can call the contain methods,
‫which will basically return true
‫whenever this element right here contains
‫the element that we pass in,
‫and so in this case, the element we pass in is e.target,
‫which is simply the element where the click happened.
‫So if the click happens on one of these elements right here,
‫then that, of course, is inside the modal,
‫and so then this whole thing here becomes false,
‫and then nothing happens,
‫but if a click happens out here,
‫then that is no longer contained here in the ref,
‫and so then this function here should get called,
‫so let's see.
‫So now, nothing is happening,
‫and as I click, I get this error,
‫and that's because it's called contains.
‫All right, let's remove that.
‫Let's click here, and then nothing happens,
‫but let's try to click outside, and beautiful.
‫So we get our log, and most importantly, the form closed,
‫so the modal window was indeed closed.
‫All right, now, there's just one problem with this,
‫because watch what happens as I click here again.
‫So that's very strange, right?
‫So we get click outside, but the window doesn't show up,
‫so what strange thing is happening here?
‫Well, the reason for this is actually the way
‫that events work in JavaScript,
‫and in particular, the fact that events bubble up.
‫So whenever I click here on this button,
‫the modal window will be attached to the DOM, right?
‫And it will be attached right here
‫as a direct child of the body,
‫and so if I click on the button,
‫that event will bubble up all the way through the DOM
‫until it also reaches that modal window,
‫and so then the click is basically detected
‫outside the modal window,
‫which will immediately close that window again,
‫so our logic is actually working just fine.
‫So again, when we click here, the modal window
‫basically gets opened for a millisecond,
‫but then it immediately detects a click outside of it,
‫and so then it will immediately close again,
‫and so the way that we fix this is to not listen
‫for these events on the bubbling phase,
‫but on the capturing phase,
‫so basically, as the event moves down the DOM tree
‫and not up the DOM tree,
‫and if all of this sounds a bit strange,
‫then this is just the way that events work in JavaScript,
‫and I have a whole lecture about this
‫in the section on how React works behind the scenes,
‫so please go back there if this is strange to you,
‫but in any case, we can change this default behavior
‫by here, passing in a third argument,
‫which is simply to set this to true.
‫And so if we use true here, then again,
‫the event will actually be handled in the capturing phase,
‫so as the event moves down the tree.
‫So then here the same thing,
‫let's just reload here,
‫let's click, and nice.
‫So that solves our problem,
‫and so that's the reason why I told you earlier
‫that even though we left vanilla JavaScript behind,
‫it's still very important to understand
‫how many things work,
‫because without that knowledge,
‫you probably wouldn't have been able to solve this issue,
‫and so that fundamental knowledge
‫is still extremely important.
‫So let's just test if the click outside still works,
‫and it does, great.
‫So this is how any modern modal window is supposed to work.
‫And now, just to finish, let's actually extract
‫all of this here into a custom hook,
‫which is a great exercise, not only
‫to practice your React skills,
‫but also to create a very nice and reusable
‫custom hook for you.
‫So if you want, you can pause the video here
‫and do this extraction yourself,
‫and then I will come back here
‫once you are finished with that.
‫All right, so hopefully, you tried this,
‫but in any case, let's create that new file right here
‫in the reusable Hooks folder,
‫and let's call it useOutsideClick.js,
‫and then a function called useOutsideClick.
‫Let's also export this,
‫and then what we need to do is to basically
‫just grab all the code here.
‫So I will just copy it for now, paste that here,
‫and then of course, we need to get everything in here.
‫So what do we need inside this custom hook from the outside?
‫Well, it's actually already marked in red,
‫which is basically this handler function right here.
‫So when we use this custom hook now in our component,
‫we will need to pass in that function,
‫and so let's actually delete this now
‫and call useOutsideClick.
‫And so again, here, we now need to pass in that handler,
‫and so that handler is going to be that close function.
‫And so here, let's receive that,
‫and then of course, here, we just call it handler,
‫'cause this should become really generic.
‫Then here, also, we need to use handler,
‫and maybe to make this even a bit more flexible,
‫let us here allow the user to specify
‫whether they want to listen for the event
‫in the bubbling or in the capturing phase.
‫So let's call this one listenCapturing,
‫and by default, let's set it to true,
‫so then here, we will replace those with listenCapturing.
‫Then we need that here like this,
‫and now, let's see the problem that we have here,
‫and yeah, the only thing that we are missing here
‫is this ref,
‫and so that means that this function here
‫should probably return that ref.
‫So let's store that here,
‫and then all we need to do is to export that,
‫so to return that actually from here,
‫and with this, the same functionality
‫should now be encapsulated here in this abstraction,
‫so in that own custom hook right there.
‫So let's see,
‫and beautiful.
‫Maybe just to finish, let's get rid of that console.log,
‫and there we go, nice.
‫And now, next up, let's also reuse this modal here
‫for some other things.
‫So this form right here, or actually, not this,
‫but here, the edit form is still being rendered down here,
‫and so let's also place that inside a modal window
‫in the next lecture.