Problem

Teams at Amazon and Mozilla use Mergify to prioritize, queue, and automatically merge their PR’s. You’ll probably like it too. Check out their Startup Program to get 12 months free.

import { useRef, useEffect } from 'react'
import {Modal, ModalHeader, ModalBody, ModalFooter} from '../components/Modal'

export default function InputModal({isOpen, close}) {
  const inputRef = useRef(null)

  useEffect(() => {
    if (isOpen) {
      inputRef.focus()
    }
  },[isOpen])

  return (
    <Modal isOpen={isOpen}>
      <ModalHeader>
        <h3>What is your name?</h3>
      </ModalHeader>
      <ModalBody>
        <input name="name" ref={inputRef} />
      </ModalBody>
      <ModalFooter>
        <Button onClick={() => close()}>Close</Button>
      </ModalFooter>
    </Modal>
  )
}
Solution

React treats the ref as a “box” that can hold a mutable value in it’s .current property, including DOM elements. Since React needs a way to update the ref’s value without completely overriding it, the ref itself is an object with a mutable .current property that can be changed any time without changing the object reference.

The bug happens when we try to .focus() the inputRef. The input DOM element is stored in the .current property, so the correct way to focus the input is to call inputRef.current.focus().

Here’s the solution -

export default function InputModal({isOpen, close}) {
  const inputRef = useRef(null)

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

  // ...

}

Refs can store more than just DOM elements - they can store any value a component needs to hold on to without causing a re-render whenever the value changes.