Help
Support Us

Refs

As we learned in the first chapter, the DOM provides an imperative API, which lets us make changes by calling functions on elements. One example where we might need to access the imperative DOM API from a Preact component would be to automatically move focus to an input element.

The autoFocus prop (or autofocus attribute) can be used to focus an input the first time it is rendered, however there are situations where we want to move focus to an input at a specific time, or in response to a specific event.

For these cases where we need to interact directly with DOM elements, we can use a feature called "refs". A ref is a plain JavaScript object with a current property that point to any value. JavaScript objects are passed by reference, which means that any function with access to a ref object can get or set its value using the current property. Preact does not track changes to ref objects, so they can be used to store information during rendering, which can then be accessed later by any function with access to the ref object.

We can see what direct usage of the ref feature looks like without rendering anything:

import { createRef } from 'preact'

// create a ref:
const ref = createRef('initial value')
// { current: 'initial value' }

// read a ref's current value:
ref.current === 'initial value'

// update a ref's current value:
ref.current = 'new value'

// pass refs around:
console.log(ref) // { current: 'new value' }

What makes refs useful in Preact is that a ref object can be passed to a Virtual DOM element during rendering, and Preact will set the ref's value (its current property) to the corresponding HTML element. Once set, we can use the ref's current value to access and modify the HTML element:

import { createRef } from 'preact';

// create a ref:
const input = createRef()

// pass the ref as a prop on a Virtual DOM element:
render(<input ref={input} />, document.body)

// access the associated DOM element:
input.current // an HTML <input> element
input.current.focus() // focus the input!

Using createRef() globally isn't recommended, since multiple renders will overwrite the ref's current value. Instead, it's best to store refs as class properties:

import { createRef, Component } from 'preact';

export default class App extends Component {
  input = createRef()

  // this function runs after <App> is rendered
  componentDidMount() {
    // access the associated DOM element:
    this.input.current.focus();
  }

  render() {
    return <input ref={this.input} />
  }
}

For function components, a useRef() hook provides a convenient way to create a ref and access that same ref on subsequent renders. The following example also shows how to use the useEffect() hook to invoke a callback after our component is rendered, in which our ref's current value will then be set to the HTML input element:

import { useRef, useEffect } from 'preact/hooks';

export default function App() {
  // create or retrieve our ref:  (hook slot 0)
  const input = useRef()

  // the callback here will run after <App> is rendered:
  useEffect(() => {
    // access the associated DOM element:
    input.current.focus()
  }, [])

  return <input ref={input} />
}

Remember, refs aren't limited to storing only DOM elements. They can be used to store information between renders of a component without setting state that would cause additional rendering. We'll see some uses for that in a later chapter.

Try it!

Now let's put this to practice by creating a button that, when clicked, focuses an input field by accessing it using a ref.

Loading...