TL;DR: Refs are simple objects of the form
{ current: //something }
. In function components, pickuseRef
, otherwise, pickcreateRef
.
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 }
- Attach a reference to a DOM Element to that ref object
// inside the render method
<audio ref={this.audioRef} />
// this.audioRef = { current: <audio /> }
- Use it
// a handler that reacts to a click
handlePlay = () => {
this.audioRef.play();
};
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} />
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()?
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} />
// ...
);
});
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');
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();
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' }
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;
}
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;
}
The useRef
implementation in the React codebase
Top comments (0)