useRef returns a persistent object with a current property. React returns the same ref object across renders, while assigning a new current value does not trigger a re-render. section 5.1 section 5.1.2
Use a ref when a component needs to retain a value without displaying every change immediately:
- Store mechanics such as setTimeout or setInterval IDs, then cancel the timer through ref.current. section 5.0 section 5.2
- Keep a mutable value across renders by assigning to ref.current; subsequent component runs can read the updated value. section 5.1.2
- Attach a ref to a JSX element so React places the DOM element in current, allowing direct access to properties and methods. section 5.3 section 5.3.1
Comparing state and refs
A counter using useState updates the interface when its updater runs. A counter using useRef can increment ref.current without changing the displayed value. When another state update finally re-renders the component, the interface reads and displays the ref's latest value. This difference makes refs suitable for values that support application mechanics rather than immediate UI output. section 5.1.1
Managing timers and focus
Presentation Mode stores the ID returned by setInterval in timerRef.current and clears it when the user stops the mode or leaves the page. A separate BookablesList ref attaches to the Next button; after a bookable changes, nextButtonRef.current.focus() restores the intended keyboard focus. section 5.2 section 5.3.1
Listing 5.3 Using a ref to set focus
import {useReducer, useEffect, useRef, Fragment} from "react";
import {sessions, days} from "../../static.json";
import {FaArrowRight} from "react-icons/fa";
import Spinner from "../UI/Spinner";
import reducer from "./reducer";
import getData from "../../utils/api";
const initialState = { /* unchanged */ };
export default function BookablesList () {
// unchanged variable setup
const nextButtonRef = useRef(); #1
useEffect(() => { /* load data */ }, []);
// remove timer effect and stopPresentation function
function changeGroup (e) { */ unchanged */ }
function changeBookable (selectedIndex) {
dispatch({
type: "SET_BOOKABLE",
payload: selectedIndex
});
nextButtonRef.current.focus(); #2
}
function nextBookable () { /* unchanged */ }
function toggleDetails () { /* unchanged */}
if (error) {
return <p>{error.message}</p>
}
if (isLoading) {
return <p><Spinner/> Loading bookables...</p>
}
return (
<Fragment>
<div>
<select value={group} onChange={changeGroup}>
{groups.map(g => <option value={g} key={g}>{g}</option>)}
</select>
<ul className="bookables items-list-nav">
{ /* unchanged */ }
</ul>
<p>
<button
className="btn"
onClick={nextBookable}
ref={nextButtonRef} #3
autoFocus
>
<FaArrowRight/>
<span>Next</span>
</button>
</p>
</div>
{bookable && (
<div className="bookable-details">
{ /* Stop button removed */ }
</div>
)}
</Fragment>
);
}
#1 Call useRef and assign the ref to the nextButtonRef variable.
#2 Use the ref to focus the Next button.
#3 Assign nextButtonRef to the ref attribute in JSX.
Browse the focused BookablesList implementation, including ref creation, JSX attachment, and the focus call. section 5.3.1


