If you need to programmatically set focus on a React element, most tutorials and StackOverflow answers will tell you to use the useEffect
hook and a ref object like this.
This works for simple cases, but it falls apart as soon as you introduce conditional rendering. For example, if you don’t show your button until some state has changed, you’ll need to add that state to the dependency array of the useEffect
hook. You also need to ensure that the effect doesn’t run every time the state changes, so you’ll need to add a condition to the effect callback. And if these conditions get out of sync, you may introduce accessibility bugs. This is a lot of boilerplate to set focus on mount!
Callback refs to the rescue
React refs are very versatile, and you’re not limited to the ref objects that useRef
and createRef
return. Instead of an object, you can pass in a callback function ref! This callback will be called with the DOM element as soon as it’s mounted, and again with null
when it’s unmounted.
This makes it easy to set focus, with a reusable helper function.
Focus on first list element
Callback refs can also be used to quickly focus on the first element in a list. Ideally, we don’t want each list item to think about focus, so we’ll handle it in the wrapper container.
We start by writing a function that can find the first focusable item in a container. This is also handled automatically in the Microsoft Fluent UI library.
This can then be used with a callback ref to focus on the first item when the list mounts.
Wait until list data is loaded
One more feature of callback refs is that they will be invoked whenever the callback changes. If we set the callback ref conditionally, then we can easily control when focus is set.
For a list of data, we wait to wait until the list renders before focusing on the first item. We can use a simple tuple to track whether the list data is ready, and only set the callback ref when it is.
I hope these tricks help you write more accessible React components!