JavaScript Curriculum
useRef
mediumThe search input needs to auto-focus when the menu loads. The video countdown needs a timer ID that persists across renders. The custom input component needs to forward its ref to the parent. All three use useRef.
useRef
useRef returns a mutable object { current: initialValue } that persists for the full lifetime of the component. Changing ref.current does NOT trigger a re-render. This makes it perfect for two different jobs: storing DOM nodes and storing mutable values.
Four useRef Use Cases
DOM access, mutable storage, previous value tracking, render counting:
useRef gives you a direct reference to a DOM node. Use it when you need to focus an input, measure an element, or trigger imperative DOM APIs.
function SearchInput() { const inputRef = useRef(null) const focusInput = () => { inputRef.current.focus() inputRef.current.select() // select all text } return ( <> <input ref={inputRef} placeholder="Search..." /> <button onClick={focusInput}>Focus</button> </> ) }
useState vs useRef — Side by Side
See exactly when each causes re-renders:
| useState | useRef | |
|---|---|---|
| Triggers re-render | Yes — always | No — never |
| Value persists | Yes (per state update) | Yes (across renders) |
| Read current value | Via state variable | Via ref.current |
| Initial value | useState(initial) | useRef(initial) |
| Mutable directly | No — must use setter | Yes — ref.current = x |
| Use for | UI data — what to show | Timers, DOM, counters |
// Interact with both countersforwardRef — Passing Refs to Children
How to let a parent hold a ref to a custom component's DOM node:
import { forwardRef } from 'react' // ✓ forwardRef wraps the component const FancyInput = forwardRef(function FancyInput( { placeholder, ...props }, ref // second parameter receives the forwarded ref ) { return ( <input ref={ref} // attach to DOM node placeholder={placeholder} {...props} /> ) }) function Parent() { const inputRef = useRef(null) return ( <> <FancyInput ref={inputRef} placeholder="Search" /> <button onClick={() => inputRef.current.focus()}> Focus from parent </button> </> ) }
Your Challenge
Write a SearchInput that: (1) auto-focuses on mount using useRef + useEffect. (2) Stores a debounce timer ID in a ref. (3) Wrap it in forwardRef so a parent component can call .focus() via a ref. Test by rendering <SearchInput ref={ref} /> and calling ref.current.focus() from a button.
Challenge
Build a SearchBar that auto-focuses on mount (useRef + useEffect). Add a stopwatch that stores the interval ID in a ref so start/stop work correctly. Wrap a custom TextInput in forwardRef so a parent can call .focus() on it.
useRef
mediumThe search input needs to auto-focus when the menu loads. The video countdown needs a timer ID that persists across renders. The custom input component needs to forward its ref to the parent. All three use useRef.
useRef
useRef returns a mutable object { current: initialValue } that persists for the full lifetime of the component. Changing ref.current does NOT trigger a re-render. This makes it perfect for two different jobs: storing DOM nodes and storing mutable values.
Four useRef Use Cases
DOM access, mutable storage, previous value tracking, render counting:
useRef gives you a direct reference to a DOM node. Use it when you need to focus an input, measure an element, or trigger imperative DOM APIs.
function SearchInput() { const inputRef = useRef(null) const focusInput = () => { inputRef.current.focus() inputRef.current.select() // select all text } return ( <> <input ref={inputRef} placeholder="Search..." /> <button onClick={focusInput}>Focus</button> </> ) }
useState vs useRef — Side by Side
See exactly when each causes re-renders:
| useState | useRef | |
|---|---|---|
| Triggers re-render | Yes — always | No — never |
| Value persists | Yes (per state update) | Yes (across renders) |
| Read current value | Via state variable | Via ref.current |
| Initial value | useState(initial) | useRef(initial) |
| Mutable directly | No — must use setter | Yes — ref.current = x |
| Use for | UI data — what to show | Timers, DOM, counters |
// Interact with both countersforwardRef — Passing Refs to Children
How to let a parent hold a ref to a custom component's DOM node:
import { forwardRef } from 'react' // ✓ forwardRef wraps the component const FancyInput = forwardRef(function FancyInput( { placeholder, ...props }, ref // second parameter receives the forwarded ref ) { return ( <input ref={ref} // attach to DOM node placeholder={placeholder} {...props} /> ) }) function Parent() { const inputRef = useRef(null) return ( <> <FancyInput ref={inputRef} placeholder="Search" /> <button onClick={() => inputRef.current.focus()}> Focus from parent </button> </> ) }
Your Challenge
Write a SearchInput that: (1) auto-focuses on mount using useRef + useEffect. (2) Stores a debounce timer ID in a ref. (3) Wrap it in forwardRef so a parent component can call .focus() via a ref. Test by rendering <SearchInput ref={ref} /> and calling ref.current.focus() from a button.
Challenge
Build a SearchBar that auto-focuses on mount (useRef + useEffect). Add a stopwatch that stores the interval ID in a ref so start/stop work correctly. Wrap a custom TextInput in forwardRef so a parent can call .focus() on it.