Functional React Refs
Markdown, and some code examples, about React refs, from a function component perspective.
- The
ref
prop useRef
- Refs and the DOM
- Store Values
- Measure DOM Node
- Callback Refs
- Setting a ref to a Function Component
useImperativeHandle
forwardRef
- Forwarding Refs
React.createRef
ref
is not a prop. Like key
, it’s handled differently by React.
Docs: https://reactjs.org/docs/hooks-reference.html#useref
const ref = useRef(initialValue)
-
ref.current
is initialized toinitialValue
. -
ref
'll persist for the full lifetime of the component. -
Passing a ref object as a DOM element's
ref
attribute (e.g.,<div ref={ref} />
) sets its.current
to the corresponding DOM node whenever that node changes:const TextInputWithFocusButton = () => { const inputRef = useRef(null) return ( <> <input type='text' ref={inputRef} /> <button onClick={() => inputRef.current.focus()}> Focus on input </button> </> ) }
-
However, it can hold any mutable value.
-
Unlike creating a
{ current: ... }
object yourself,useRef()
gives the same ref object on every render. -
useRef
doesn’t notify on content change. Mutating.current
won’t cause a re-render. To run some code on a ref attach/detach (to/from a DOM node), you may want to use a callback ref instead.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
To allow taking a ref
to your function component, you can use
forwardRef
(possibly in conjunction with
useImperativeHandle
), or you can convert the component to a class.
You can, however, use the ref
attribute inside a function component as long as you refer to a DOM element or a class component.
In rare cases, you might want to have access to a child’s DOM node.
If you use React 16.3 or higher, we recommend to use ref forwarding for these cases. Ref forwarding lets components opt into exposing any child component’s ref as their own.
If you use React 16.2 or lower, or if you need more flexibility than provided by ref forwarding, you can explicitly pass a ref as a differently named prop.
Docs: https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables
The useRef()
Hook isn’t just for DOM refs.
The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.
const logTick = () => console.log('Tick')
const Timer = () => {
const intervalRef = useRef()
const start = () => intervalRef.current = setInterval(logTick, 1000)
const stop = () => clearInterval(intervalRef.current)
return (
<>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</>
)
}
Docs: https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
We can use callback ref. React will call that callback whenever the ref gets attached to a different node:
const MeasureExample = () => {
const [height, setHeight] = useState(0)
const measuredRef = useCallback(node => {
if (node !== null)
setHeight(node.getBoundingClientRect().height)
}, [])
return (
<>
<p ref={measuredRef}>Hi!</p>
The above paragraph is {height}px tall
</>
)
}
Also see following example.
Docs: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
React also supports another way to set refs called “callback refs”, which gives more fine-grain control over when refs are set and unset.
The callback receives the React component instance or HTML DOM element, which can be stored and accessed elsewhere.
const CustomTextInput = () => {
let inputRef
const setInputRef = useCallback(element => inputRef = element, [])
return (
<>
<input type='text' ref={setInputRef} />
<button onClick={() => inputRef.focus()}>
Focus text input
</button>
</>
)
}
Also see previous example.
Docs: https://reactjs.org/docs/hooks-faq.html#can-i-make-a-ref-to-a-function-component
While you shouldn’t need this often, you may expose some imperative methods to a parent component with the
useImperativeHandle
Hook.
Docs: https://reactjs.org/docs/hooks-reference.html#useimperativehandle
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
customizes the instance value that is exposed to parent components when using ref
.
As always, imperative code using refs should be avoided in most cases.
useImperativeHandle
should be used with forwardRef
:
FancyInput.jsx
:
const FancyInput = (props, ref) => {
const inputRef = useRef()
useImperativeHandle(
ref,
() => ({
focus: () => inputRef.current.focus()
})
)
return <input ref={inputRef} />
}
export default forwardRef(FancyInput)
FancyInputParent.jsx
:
const FancyInputParent = () => {
const fancyInputRef = useRef()
return (
<>
<FancyInput ref={fancyInputRef} />
<button onClick={() => fancyInputRef.current.focus()}>
Focus on FancyInput
</button>
</>
)
}
Docs: https://reactjs.org/docs/react-api.html#reactforwardref
React.forwardRef
creates a React component that forwards theref
attribute it receives to another component below in the tree.- This technique is not very common but is particularly useful in two scenarios:
- Forwarding refs to DOM components
- Forwarding refs in higher-order-components
React.forwardRef
accepts a rendering function, which... :- React calls with
props
andref
arguments, and - - should return a React node.
- React calls with
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} onClick={props.onClick}>
{props.children}
</button>
))
const FancyButtonParent = () => {
const ref = useRef()
return (
<FancyButton ref={ref} onClick={() => console.log(ref.current)}>
Click Me!
</FancyButton>
)
}
Docs: https://reactjs.org/docs/forwarding-refs.html
Ref forwarding is a technique for automatically passing a ref through a component to one of its children. This is typically not necessary for most components in the application.
Most common use cases:
Usually a component won't obtain a ref to a child's inner DOM element / component.
However, highly reusable “leaf” components like FancyButton
or MyTextInput
tend to be used throughout the application in a similar manner as a regular DOM button
and input
, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations.
To allow "forwarding" a ref via FancyButton
to its inner button
element, do this:
const FancyButton = forwardRef((props, ref) => (
<button ref={ref}>
{props.children}
</button>
))
// You can now get a ref directly to the DOM button:
const ref = useRef()
<FancyButton ref={ref}>Click me!</FancyButton>
Note
The second ref
argument only exists when you define a component with React.forwardRef
call.
Regular function or class components don’t receive the ref
argument, and ref is not available in props either.
Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.
Forwarding refs in HOCs
Say we have a HOC that logs all props of the wrapped component to the console.
To log the wrapped component's ref
attribute correctly, we have to use ref forwarding:
const logProps = WrappedComponent => {
const LogProps = props => {
useEffect(() => console.log(props))
const { forwardedRef, ...rest } = props
return <WrappedComponent ref={forwardedRef} {...rest} />
}
return forwardRef((props, ref) => <LogProps {...props} forwardedRef={ref} />)
}
Also see Displaying a custom name in DevTools.
Docs: https://reactjs.org/docs/react-api.html#reactcreateref
React.createRef
creates a ref that can be attached to React elements via the ref
attribute.