Skip to content

Markdown, and some code examples, about React refs, from a function component perspective.

Notifications You must be signed in to change notification settings

Shlomo-Moller/hook-ref

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Functional React Refs

Markdown, and some code examples, about React refs, from a function component perspective.




Table of Contents




Note on the ref prop

Go top | Previous | Next

ref is not a prop. Like key, it’s handled differently by React.




useRef

Docs: https://reactjs.org/docs/hooks-reference.html#useref

Go top | Previous | Next

const ref = useRef(initialValue)
  • ref.current is initialized to initialValue.

  • 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.




Refs and the DOM

Docs: https://reactjs.org/docs/refs-and-the-dom.html

Go top | Previous | Next

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.




Store Values

Docs: https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables

Go top | Previous | Next

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.

Example:

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>
		</>
	)
}



Measure DOM Node

Docs: https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node

Go top | Previous | Next

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.




Callback Refs

Docs: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

Go top | Previous | Next

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.




Setting a ref to a Function Component

Docs: https://reactjs.org/docs/hooks-faq.html#can-i-make-a-ref-to-a-function-component

Go top | Previous | Next

While you shouldn’t need this often, you may expose some imperative methods to a parent component with the useImperativeHandle Hook.




useImperativeHandle

Docs: https://reactjs.org/docs/hooks-reference.html#useimperativehandle

Go top | Previous | Next

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>
		</>
	)
}



React.forwardRef

Docs: https://reactjs.org/docs/react-api.html#reactforwardref

Go top | Previous | Next

  • React.forwardRef creates a React component that forwards the ref 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 and ref arguments, and -
    • should return a React node.
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>
	)
}



Forwarding Refs

Docs: https://reactjs.org/docs/forwarding-refs.html

Go top | Previous | Next

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:

Forwarding refs to DOM components

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.




React.createRef

Docs: https://reactjs.org/docs/react-api.html#reactcreateref

Go top | Previous

React.createRef creates a ref that can be attached to React elements via the ref attribute.