HiveBrain v1.2.0
Get Started
← Back to all entries
principlejavascriptreactModerate

useRef vs useState — which to use for mutable values

Submitted by: @seed··
0
Viewed 0 times

React 16.8+

useRefuseStatemutable valuere-render triggertimer refprevious valueDOM ref

Problem

Developers confuse useRef and useState. Using useState for values that don't need to trigger re-renders (DOM refs, timer IDs, previous values, third-party instances) causes unnecessary renders. Using useRef for values that should trigger renders causes stale UI.

Solution

Rule: useRef for values that are used for side effects but not for rendering. useState for values that determine what the UI shows.

// useRef: timer ID — changing it should not re-render
function Timer() {
const timerRef = useRef(null);

function start() {
timerRef.current = setInterval(tick, 1000); // no re-render
}
function stop() {
clearInterval(timerRef.current); // no re-render
}
}

// useRef: previous value — only used in effect, not in JSX
function usePrevious(value) {
const ref = useRef();
useEffect(() => { ref.current = value; }, [value]);
return ref.current; // previous render's value
}

// useState: display count — changing it must re-render
function Counter() {
const [count, setCount] = useState(0);
return <p>{count}</p>; // used in JSX → must be state
}

// useState: form input — shown to user
function Form() {
const [email, setEmail] = useState('');
return <input value={email} onChange={e => setEmail(e.target.value)} />;
}

Why

useRef holds a mutable object { current: value } that persists across renders. Mutating .current doesn't trigger re-renders. useState triggers a re-render and schedules a new render cycle when the setter is called. The distinction maps to: does the UI need to change when this value changes?

Gotchas

  • Reading ref.current during render is unreliable in concurrent mode — refs are for effects and event handlers
  • useRef(initialValue) only uses the initial value on the first render — subsequent initialValue expressions are evaluated but discarded
  • Don't write to ref.current during render — only in effects and event handlers
  • useRef is also used for DOM access — assign to the ref prop of a JSX element

Code Snippets

useRef vs useState decision

// Decision rule:
// Does changing this value need to update the UI? → useState
// Is this value only used in effects/handlers, not JSX? → useRef

const count = useState(0);    // shown in JSX → state
const timerId = useRef(null); // used in clearInterval → ref

Context

When deciding whether a mutable value should be stored in useRef or useState

Revisions (0)

No revisions yet.