Functional React Refs
Markdown, and some code examples, about React refs, from a function component perspective.
- The
refprop useRef- Refs and the DOM
- Store Values
- Measure DOM Node
- Callback Refs
- Setting a ref to a Function Component
useImperativeHandleforwardRef- 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.currentis initialized toinitialValue. -
ref'll persist for the full lifetime of the component. -
Passing a ref object as a DOM element's
refattribute (e.g.,<div ref={ref} />) sets its.currentto 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. -
useRefdoesn’t notify on content change. Mutating.currentwon’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.forwardRefcreates a React component that forwards therefattribute 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.forwardRefaccepts a rendering function, which... :- React calls with
propsandrefarguments, 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.