Hooks are functions that can be called in functional components that allow you to use state and other React features without writing a class.

Commonly used hooks include the following:

  • useState
  • useEffect
  • useRef
  • useContext
  • useReducer

This article focuses on how useRef can detect clicks outside elements

useRef

UseRef is a very powerful base hook. Here are three main application scenarios:

  • Gets the DOM element node
  • Gets an instance of the child component
  • Data that shares data between render cycles (state cannot store data across render cycles because saving state triggers rerendering of components)

Here we use useRef to get the actual DOM node

The useRef function takes a variable for the initial value of ref and returns a REF object

const elementRef = useRef(null)
Copy the code

You then need to add the REF attribute value to the corresponding JSX node, which is the ref object returned by the call to useRef. In this case, you need to get the real DOM, just get the current property of ref

const DOM = elementRef.current;
Copy the code

Details are as follows:

function Index() {
  const elementRef = useRef(null)

  return (
    <>
      <div ref={elementRef}>
        Hello World
      </div>
    </>)}Copy the code

The specific implementation

Let’s check to see if a click event occurred outside the element

Here are two scenarios where we need to check for clicks outside elements:

  • When we create a popover, when we click on the popover, we need to close the popover
  • When you create adropdownWhen clickdropdownNeed to close it

Here’s a simple example:

function Index() {
    const [isOpen, setIsOpen] = useState(true)
    return (
        <>
            <div>
                <h2>App with a Modal</h2>
                <button onClick={()= > setIsOpen(true)}>Open Modal</button>
                <div id="modal">
                  <Modal isOpen={isOpen}>Modal component content</Modal>
                </div>
        </>)}Copy the code

Click the button in the above component to show Modal. So our goal is to turn off Modal when you click outside of Modal.

Here’s how to do it:

  1. Use ref to reference the Modal component

  2. Detection of click

  3. Verify that a click occurs outside of the Modal component

  4. If a click occurs outside of the Modal component, setIsOpen(false)

Step 1: Reference Modal

First use useRef to reference the Modal node

function Index() {
  const [isOpen, setIsOpen] = useState(false);
  const modalRef = useRef();

  return (
    <>
      <div>
        <h2>App with a Modal</h2>
        <button onClick={()= > setIsOpen(true)} type="button">
          Open Modal
        </button>
        <div id="modal" ref={modalRef}>
          <Modal isOpen={isOpen}>This is a Modal</Modal>
        </div>
      </div>
    </>
  );
}
Copy the code

When the current component is rendered, we can retrieve the DOM node via modalref.current

Step 2: Add global click event listeners

The second step adds an event listener globally

useEffect(() = > {
  function handler(event) {
    console.log(event, 'clicked somewhere')}window.addEventListener('click', handler)
  return () = > window.removeEventListener('click', handler)
}, [])
Copy the code

We added click listening to Windows to listen for events on the entire page. Note that it is important to remove bound global events when a component is removed, otherwise it may cause memory leaks or unknown errors

Step 3: Check if a click occurs outside the element

When an Event is clicked, the input parameter to the callback function is the Event object, which contains a list of information about the clicked Event. If you want to get the currently clicked element, use event.target. The following checks if the modal element contains event.target:

useEffect(() = > {
  function handler(event) {
    if(! modalRef.current? .contains(event.target)) {console.log('clicked outside of the modal')}}window.addEventListener('click', handler)
  return () = > window.removeEventListener('click', handler)
}, [])
Copy the code

Step 4: Turn off Modal when clicked outside of Modal

When detecting an out-of-modal click, execute setIsOpen(false) to close the popover

useEffect(() = > {
  function handler(event) {
    if(! modalRef.current? .contains(event.target)) {console.log('clicked outside of the modal')}}window.addEventListener('click', handler)
  return () = > window.removeEventListener('click', handler)
})
Copy the code

To encapsulate the hooks

Let’s package the above functionality as a new hook:

export function useOnClickOutside(ref, callback) {
  useEffect(() = > {
    function handler(event) {
      if(! ref.current? .contains(event.target)) { callback(); }}window.addEventListener('click', handler);

    return () = > window.removeEventListener('click', handler)
  }, [callback, ref]);
}
Copy the code

In the above hook, we need to pass in two arguments: ref: the ref object callback that needs to have the clickOutside effect: The callback function that triggers clickOutside has a listener event bound to the window that determines if the clicked element is in the ref corresponding DOM structure: if it is not, the callback that is passed in is triggered.

Below is a function that uses useOnClickOutside hook

function Index() {
  const [isOpen, setIsOpen] = useState(false);
  const ref = useRef(null);
  useOnClickOutside(ref, () = > setIsOpen(false));
  return (
    <div>
      <h2>App with ad Modal</h2>
      <button type="button" onClick={()= > setIsOpen(false)}>
        Open Modal
      </button>
      <div ref={ref} id="modal">
        <Modal isOpen={isOpen}>This is a Modal</Modal>
      </div>
    </div>
  );
}
Copy the code

In the above code, the same function is implemented. Add the following code that needs to be changed

useOnClickOutside(ref, () = > setIsOpen(false));
Copy the code

The use of useRef goes far beyond that. We usually use it to hold variables. Here’s how useRef and useState are used. In hooks, both useState and useRef can be used to hold variables, except that components are not rerendered when the value of useRef changes.

Github address: github.com/skychenbo/s… Welcome to attention