useState Hook: State and Re-renders
Call useState to add local state to a component, update state with the setter function, and understand when and why React re-renders.
useState Hook: State and Re-renders is a free Frontend Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is State?
State is data that changes over time and should cause the UI to update when it does. Unlike regular variables, state changes trigger React to re-render the component with the new value.
useState Syntax
useState(initialValue) returns a tuple: the current value and a setter function. Destructure it immediately.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // initial value: 0
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}TypeScript: Typing useState
TypeScript infers the type from the initial value. When the initial value is ambiguous (null, or an empty array), add an explicit type parameter.
const [count, setCount] = useState(0); // inferred: number
const [name, setName] = useState(''); // inferred: string
const [user, setUser] = useState<User | null>(null); // explicit
const [items, setItems] = useState<string[]>([]); // explicitRe-render on State Change
Calling the setter function schedules a re-render. React calls your component function again with the new state value. The returned JSX replaces the previous render in the virtual DOM.
State Updates Are Asynchronous
State updates are batched and applied before the next render, not immediately. Don't read updated state in the same event handler — the new value isn't available until the next render.
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // still 0 — update hasn't applied yet
}Functional Updates
When the new state depends on the previous state, use the functional form of the setter. This avoids stale closure bugs.
// Risky (stale closure in async callbacks):
setCount(count + 1);
// Safe (functional update):
setCount(prev => prev + 1);
// Important for buttons that fire rapidly:
function increment() {
setCount(c => c + 1); // always uses the latest value
}State with Objects
When state is an object, always create a new object — don't mutate the existing one. React compares by reference; mutations won't trigger re-renders.
const [user, setUser] = useState({ name: 'Alice', age: 30 });
// WRONG: mutates state — React won't re-render
user.age = 31;
setUser(user); // same reference!
// CORRECT: new object
setUser({ ...user, age: 31 });Multiple State Variables
Call useState multiple times for independent state variables. This is cleaner than one big state object when values don't change together.
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);Lazy Initialisation
If the initial state requires expensive computation, pass a function to useState. It runs only on the first render, not on every re-render.
// Runs getInitialState() on every render (bad if expensive):
const [state, setState] = useState(getInitialState());
// Lazy init: only runs once:
const [state, setState] = useState(() => getInitialState());When Not to Use State
Not every variable needs to be state. Derived values (total = items.reduce(…)) should be computed during render, not stored in state. State derived from props is usually a sign of over-engineering.
State Lifting
When two sibling components need to share state, lift it to their common ancestor and pass it down as props. This is the fundamental React state management pattern before reaching for context or a store.
Quick Check
Why should you use the functional form setCount(prev => prev + 1) instead of setCount(count + 1)?
Recap: useState
useState returns [value, setter]. Initial value sets the type (or use explicit generic). Setter triggers re-render. Use functional updates when new state depends on old state. Objects: always spread to new objects, never mutate. Lazy init for expensive computations. Lift state to common ancestors when siblings need to share it.
Frequently asked questions
Is the “useState Hook: State and Re-renders” lesson free?
Yes — the full text of “useState Hook: State and Re-renders” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “useState Hook: State and Re-renders”?
Call useState to add local state to a component, update state with the setter function, and understand when and why React re-renders. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “useState Hook: State and Re-renders” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- JSX: Syntax and Transpilation
- Functional Components and Props
- useState Hook: State and Re-renders
- Lists Keys and Conditional Rendering