DEV Community

Cover image for Refs in React, basic objects
Nicky Meuleman
Nicky Meuleman

Posted on • Originally published at nickymeuleman.netlify.com

Refs in React, basic objects

TL;DR: Refs are simple objects of the form { current: //something }. In function components, pick useRef, otherwise, pick createRef.

In React, a ref is an object with a mutable current property.
That's it. You can view the implementations below but they boil down to this:
ref = { current: //something }

That current property is often set to hold a reference to a DOM Element, like an <audio>tag

Getting a reference to an element allows you to directly give it commands.
This is useful for controlling media playback, managing focus and more.

It might be tempting to do this everywhere, but resist that temptation.

Basic usage

The goal is to get a reference to an <audio>tag.
This will allow you to call .play() and .pause() on that element.

There are quite a few ways of achieving the same goal.

The steps are always the same however.

Several examples can be viewed in this codesandbox demo

Code shown: using createRef in a class based component with a constructor

  • Create the ref object
// inside the constructor
this.audioRef = React.createRef();
// this.audioRef = { current: null }
Enter fullscreen mode Exit fullscreen mode
  • Attach a reference to a DOM Element to that ref object
// inside the render method
<audio ref={this.audioRef} />
// this.audioRef = { current: <audio /> }
Enter fullscreen mode Exit fullscreen mode
  • Use it
// a handler that reacts to a click
handlePlay = () => {
  this.audioRef.play();
};
Enter fullscreen mode Exit fullscreen mode

Other usecases

Referring to React Components

The examples above showed how to get a reference to a DOM Element.
That's not the only thing refs are good for. They can also point to a React Component object.

Important note: you can only reference class based components this way

Referring to a DOM Element inside a React Component

What about when you made yourself a fancy Input component and want a ref to point directly at the <input>tag so you can call inputRef.current.focus()?

<Input ref={inputRef} />
Enter fullscreen mode Exit fullscreen mode

There is a problem! It either points to the React Component object if your Input is a class component, or it throws a big fat error if your Input is a function component.

That big fat error is actually really helpful:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Enter fullscreen mode Exit fullscreen mode

React.forwardRef receives a function that gets called with the props and the ref your component received. It returns what your component would normally return. There, you can attach that ref to the specific DOM Element you want.

// in the file you want to use the ref
import Input from './Input';

function App() {
  const inputRef = React.useRef();

  // ...

  return (
    <div>
      <Input />
      {/* attach the ref to our Input component */}
      <Input ref={inputRef} />
      <Input />
    </div>
  );
}

// in your Input.js file
export default React.forwardRef((props, ref) => {
  return (
    // ...
    // forward the ref our Input component received to the HTML <input/>tag
    <input ref={ref} placeholder={props.placeholder} />
    // ...
  );
});
Enter fullscreen mode Exit fullscreen mode

This codesandbox demo shows three Input components.
One <input />tag is focussed through a ref a component received and forwarded using React.forwardRef.

Having to wrap your function component in React.forwardRef can become quite tedious.
In the future, this usage of forwardRef might become the default behaviour.

createRef vs. useRef

While createRef and useRef are very similar. There are some subtle differences.

useRef is a so called hook and is only available in function based components.

// in a function component
const myRef = React.useRef('my initial value');
Enter fullscreen mode Exit fullscreen mode

useRef creates an object { current : initialValue } and returns it the first time it gets called. If that object already exists, useRef will return that existing object instead.

createRef will create an object { current : null } each time it is called.

// in the constructor of a class based component
this.myRef = React.createRef();
// even in a function based component
const myRef = React.createRef();
Enter fullscreen mode Exit fullscreen mode

Instance variables

In class based components, if you want a value that can change but doesn't trigger a render cycle. An instance variable is what you want. eg: this.palindrome = 'racecar' and later this.palindrome='pullup'

In function components, useRef can fill that role!

Since useRef(initialValue) returns an object with a mutable current property.
There is nothing stopping us from using it like an instance variable.

const palindrome = useRef('racecar');
// const palindrome = { current: 'racecar' }
// ...
palindrome.current = 'pullup';
// const palindrome = { current: 'pullup' }
Enter fullscreen mode Exit fullscreen mode

This demo uses useRef to point to the <form> DOM element and to an internal count variable.

Implementations

The implementation of both createRef and useRef in the official React codebase is surprisingly simple.

createRef

export function createRef() {
  const refObject = {
    current: null
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}
Enter fullscreen mode Exit fullscreen mode

The createRef implementation in the React codebase

useRef

export function useRef(initialValue) {
  currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
  workInProgressHook = createWorkInProgressHook();
  let ref;

  if (workInProgressHook.memoizedState === null) {
    ref = { current: initialValue };
    if (__DEV__) {
      Object.seal(ref);
    }
    workInProgressHook.memoizedState = ref;
  } else {
    ref = workInProgressHook.memoizedState;
  }
  return ref;
}
Enter fullscreen mode Exit fullscreen mode

The useRef implementation in the React codebase

Top comments (0)