[React][Hook] Store Values Without Re-renders and Control the DOM with useRef
Use useRef when you need a value that survives re-renders without updating the UI, or when you need a React-friendly way to focus and control DOM nodes.

1. Environment
- OS: Windows 11 / macOS
- Frontend library: React.js (v18+)
- Core concept: React Hooks (
useRef,useState) - IDE: Visual Studio Code (VS Code)
2. Problem
When a component needs to remember data, useState is usually the first tool that comes to mind. But useState always re-renders the screen when the value changes.
So what do you do when you must keep a value but do not need a UI update? And in React, how do you reach a specific DOM node the way vanilla JS uses document.getElementById()?
3. Cause
Suppose you only need to track “how many times the button was pressed|–internally, or run a background timer that ticks every second. If you store that in useState, the whole component re-renders on every increment - even when nothing on screen depends on that number.
Those useless re-renders hurt performance. You need a way to update a value quietly without touching the UI.
4. Solution
1) Store a value without re-rendering (useState vs useRef)
This example shows the core difference:
- Increase Ref raises the console value but the UI stays the same.
- Later, Increase State re-renders the screen, and the accumulated Ref value appears in one shot.
import React, { useState, useRef } from 'react';
function Counter() {
const [stateCount, setStateCount] = useState(0);
// 1. Create a ref object with initial value 0
const refCount = useRef(0);
const increaseState = () => {
setStateCount(stateCount + 1);
console.log('State updated! The screen re-renders immediately.');
};
const increaseRef = () => {
// Always read/write through .current
refCount.current = refCount.current + 1;
console.log('Ref updated! Value rose, UI did not:', refCount.current);
};
return (
<div>
<p>State count: {stateCount}</p>
<p>Ref count: {refCount.current}</p>
{/* Every click updates the number on screen */}
<button onClick={increaseState}>Increase State</button>
{/* UI stays put; the value still accumulates inside */}
<button onClick={increaseRef}>Increase Ref</button>
</div>
);
}
Important: Do not write refCount = refCount + 1. useRef(0) does not return the number 0“it returns an object shaped like { current: 0 }. Read and update the real value only through .current.
2) Access a DOM node (auto focus)
When a “Write post|–button should put the cursor in the title field, attach a ref to the input and call focus():
import React, { useRef, useEffect } from 'react';
function AutoFocusInput() {
// 1. Create a ref (initial value null)
const inputRef = useRef(null);
useEffect(() => {
// 3. After paint, inputRef.current points at that <input>
// Same idea as document.getElementById(...).focus() in vanilla JS
inputRef.current.focus();
}, []);
return (
<div>
{/* 2. Wire the ref prop to the DOM node you need */}
<input ref={inputRef} type="text" placeholder="Auto-focused here!" />
<button onClick={() => inputRef.current.focus()}>Focus again</button>
</div>
);
}
5. Summary
Component variables compared: useState, useRef, and a plain let:
| Re-renders on change | Survives re-render | Typical use | |
|---|---|---|---|
| useState | Yes | Yes | Values that must show on screen |
| useRef | No | Yes | Hidden internal data; DOM access |
Plain let |
No | No (resets) | Temporary values fine to reset each render |


