Remembering Values with useRef
Remembering Values with useRef
A teacher needs an internal count of register checks without redrawing the interface for every check. Only an explicit report action should display the latest count.
useRef returns a stable object with a current property. Changing current remembers a value across renders but does not schedule a render. Use state for information that must immediately affect the displayed interface.
Understand
Remember an event-time value in ref.current.
Build
Explain why ref changes do not update displayed text by themselves.
Check
Use state when publishing a value to the screen.
Remember privately, publish deliberately
Record check
An event increments ref.current.
No redraw request
The ref change alone does not schedule rendering.
Show report
A state update publishes the latest count.
A ref is a container, not a state setter
React gives the component the same ref object across its renders. You can change its current property from an event handler to remember information such as a timer handle or the latest imperative operation. A local variable would be created again when the function renders; the ref container is retained for that component identity.
A state update asks React to render with new information. A ref assignment does not. If the screen should show every attendance check immediately, state is the appropriate tool. This example intentionally separates an internal record from a displayed report, so the teacher can see what the difference means.
Publish an internal check count
checks remembers event-time changes. reported is state because its value appears in JSX. Record check updates only the ref. Show report reads the ref in an event handler and stores that value in state, which makes a new rendered report possible.
React · JavaScript with JSX
import { useRef, useState } from 'react';
export default function App() {
const checks = useRef(0);
const [reported, setReported] = useState(0);
return <main><h1>Register checks</h1><p>Reported checks: {reported}</p>
<button onClick={() => { checks.current += 1; }}>Record check</button>
<button onClick={() => setReported(checks.current)}>Show report</button>
</main>;
}Expected screen and behavior
Record check remembers a check while the displayed report stays 0. Show report displays the latest remembered count.Separate memory from a render request
React · Publish an internal check count
import { useRef, useState } from 'react';
export default function App() {
const checks = useRef(0);
const [reported, setReported] = useState(0);
return <main><h1>Register checks</h1><p>Reported checks: {reported}</p>
<button onClick={() => { checks.current += 1; }}>Record check</button>
<button onClick={() => setReported(checks.current)}>Show report</button>
</main>;
}The first render creates the retained ref container with current equal to zero and state reported equal to zero. Clicking Record check mutates current to one. The handler finishes without calling a state setter, so this mutation does not request a new rendered description.
Clicking Show report reads one from current and calls setReported. React renders with reported equal to one. The ref object remains the same retained container, so further checks continue from its remembered value. The displayed report may stay behind again until the teacher publishes another snapshot. That delay is part of this example’s requirement.
Choose refs for nonvisual information
A timeout identifier is a useful ref value because changing the identifier is not a visible school fact. Storing it lets a later event cancel the scheduled work. A DOM node is another useful ref value, allowing an event to focus a field without making the node itself state.
Avoid reading or writing mutable refs during rendering except for documented initialization patterns. A render should describe the interface from its inputs consistently. If the displayed text secretly reads mutable event data from a ref, unrelated rerenders can reveal changes at surprising times. Keep visible information in state as this report does.
Try the school task
React · starting example
import { useRef, useState } from 'react';
export default function App() {
const checks = useRef(0);
const [reported, setReported] = useState(0);
return <main><h1>Register checks</h1><p>Reported checks: {reported}</p>
<button onClick={() => { checks.current += 1; }}>Record check</button>
<button onClick={() => setReported(checks.current)}>Show report</button>
</main>;
}Add a Reset checks button that clears both the internal count and the displayed report. After recording twice and showing the report, reset should display zero. Recording once after reset and showing the report should display one, proving the internal value also restarted.
Worked answer
The answer resets ref.current in an event handler and separately sets the visible state to zero. Resetting only the report would leave old checks in the internal container, so the next Show report would unexpectedly bring them back. Both operations are meaningful here because they represent different kinds of memory.
React · JavaScript with JSX
import { useRef, useState } from 'react';
export default function App() {
const checks = useRef(0);
const [reported, setReported] = useState(0);
function reset() { checks.current = 0; setReported(0); }
return <main><h1>Reset register checks</h1><p>Reported checks: {reported}</p>
<button onClick={() => { checks.current += 1; }}>Record check</button>
<button onClick={() => setReported(checks.current)}>Show report</button>
<button onClick={reset}>Reset checks</button>
</main>;
}Expected screen and behavior
Record twice and show the report to see 2. Reset checks displays 0 and clears the internal counter.Mistakes and boundaries
A ref survives rerenders of the same component, not every refresh or identity replacement. It is not persistent storage, and separate component instances receive separate ref containers. Do not use a module variable instead when each classroom widget needs its own internal value.
Using a ref to hide a reactive value from an Effect’s dependency list can produce stale synchronization. Refs are a tool for appropriate mutable memory, not a general repair for missing dependencies. Decide first whether the value is a visible fact, an external handle or something that must resynchronize an Effect.
Check your reasoning
React · Publish an internal check count
import { useRef, useState } from 'react';
export default function App() {
const checks = useRef(0);
const [reported, setReported] = useState(0);
return <main><h1>Register checks</h1><p>Reported checks: {reported}</p>
<button onClick={() => { checks.current += 1; }}>Record check</button>
<button onClick={() => setReported(checks.current)}>Show report</button>
</main>;
}Choose the answer that follows the data and the interaction, then read the explanation for each alternative.
Carry the idea forward
Use refs for retained information outside the rendered data flow and use state for visible updates. Continue with working with DOM refs to apply this container to a browser element.
Read the primary reference: React: useRef reference.
Reaching the recap and completing the quizzes are separate milestones.