简体   繁体   中英

Using useRef to focus an element, migrating class to hooks question?

Does anyone knows hows the right approach to pass callback references using react hooks. I'm trying to convert a modal that is built in a class component, to a hook component, but I'm not sure what's the correct way to do it.

  onOpen = () => {
   this.setState({ isOpen: true }, () => {
    // Ref for the button
    this.closeButtonNode.focus();
   });
   this.toggleScrollLock();
  };

And this is how I pass the the reference in the code

     <ModalContent
      buttonRef={(n) => {
        this.closeButtonNode = n;
      }}
      {// More props...}
    />

And the modal content component has the buttonRef like this

         <button
          type="button"
          className="close"
          aria-labelledby="close-modal"
          onClick={onClose}
          ref={buttonRef}
        >
          <span aria-hidden="true">×</span>
        </button>

So when the modal pops I was able to get focus in my button close, with hooks, the only way I managed to replicate the behavior is to add an useEffect hook that listen to the isOpen state like this:

useEffect(() => {
 if (isOpen) closeButtonNode.current.focus();
}, [isOpen]);

const onOpen = () => {
 setIsOpen(true);
 toggleScrollLock();
};

And this is how I pass the prop

  const closeButtonNode = useRef(null);
  return (
  <ModalContent
    buttonRef={closeButtonNode}
    {// More props...}
  />
  )

And I just use it like a regular ref, without passing a callback function, this works but I wonder why it works that way and why I cannot set the focus on the onOpen function like the class based component.

This is the sandbox if you want to check the full code. https://codesandbox.io/s/hooks-modal-vs-class-modal-bdjf0

Why I cannot set the focus on the onOpen function like the class based component

Because when onOpen function get called open toggle still false and it will get updated after the modal is already opened. Please note that useState doesn't have a second argument (callback) like setState in class based component as you have done to set the focus. That why you needed to use useEffect .

You can test that by setting a delay using setTimeout after you set open to true like so:

const onOpen = () => {
  setIsOpen(true);
  setTimeout(() => closeButtonNode.current.focus())
};

Although, your approach using useEffect would be maybe better option.

codeSandbox example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM