DOM and Refs

DOM and Refs

Not all user interfaces can be achieved using purely "React", to re-render a child you would render it with new props, but sometimes the thing you want to do is not possible using props or state. In this case, we may need to use Refs, this provides a way to access DOM nodes, or React elements created by the render method. The most common use case for refs is managing focus of a button or input field. For example, rendering a modal and focusing the first input field so the transition for keyboard users is smooth. Refs can also be used for text selection, media playback, triggering animations and integrating with third-party DOM libraries which don't use React. In an ideal world, we don't want to use refs.

Below we have a simple example of a button, which on first render focuses. We can just hit our enter key and it will increase the count rather than manually using our cursor.

import { useEffect, useRef, useState } from 'react' export default function FocusedButton() { const [count, setCount] = useState(0) const buttonRef = useRef(null) useEffect(() => { // As this component is rendered, focus the button. Instead of clicking this button // here, I could just spam my "return" key, and it'd fire the event as well. buttonRef.current.focus() }, []) const handleClick = () => setCount(count + 1) return ( <button onClick={handleClick} ref={buttonRef}> Current count: {count} </button> ) }

One common theme from what I've noticed is to try and figure out the problem first with state, or props, then if all else fails use refs. Basically only use refs when the behaviour isn't controllable in React.

Forwarding Refs

React can't pass refs as a prop to functional components, "Function components cannot be given refs. Attempts to access this ref will fail". However, we can use React.forwardRef(), which creates a React component and accepts two arguments; props and ref. This is similar to lifting state I suppose, you can have a parent with a ref, and pass it to this child component, which uses forwardRef().

React.forwardRef((props, ref) => <button ref={ref}>{props.children}</button>)