
React useState Hook Explained with Examples
- bilalshafqat42
- June 18, 2025
- React Js
- 0 Comments
Introduction: What is useState?
React is a powerful JavaScript library for building interactive user interfaces — and at the core of its interactivity lies something called state. State is what allows your app to change over time, respond to user actions, and update the UI dynamically.
The useState()
hook is the simplest and most commonly used React hook. It enables function components to store and update state — previously only possible in class components. If you’re using modern React (and you are), understanding useState
is non-negotiable.
In this guide, you’ll learn:
- What
useState()
does - How to use it
- When to use it
- Common patterns and mistakes
- Best practices for 2025
What is useState()?
useState()
is a function from the React library that allows you to add local state to functional components.
Before hooks were introduced in React 16.8, managing state was only possible in class-based components. Now, with useState()
, you can write cleaner, smaller, and more maintainable functional components — with built-in reactivity.
Basic Syntax
const [state, setState] = useState(initialValue);
state
: the current state valuesetState
: a function used to update the stateinitialValue
: the default value for the state when the component mounts
Example 1: Basic Counter
Let’s look at the most common example — a button that increments a number on click.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
Each time the button is clicked, setCount
updates the state, and the component re-renders with the new value.
State Can Be Any Type
The beauty of useState
is that it works with any data type — strings, booleans, arrays, objects, or even null.
Examples
const [name, setName] = useState("React");
const [isOpen, setIsOpen] = useState(false);
const [todos, setTodos] = useState([]);
const [user, setUser] = useState({ name: "", email: "" });
You can use useState
to control everything from dropdown visibility to form data and list items.
Updating State Correctly
To update state, always use the updater function (setState
) — never mutate the state directly.
Example
setCount(count + 1);
// For updates that depend on previous state:
setCount(prev => prev + 1);
This avoids bugs related to stale closures, especially when calling setState
multiple times in a single event.
Avoid Direct Mutation
One of the most common mistakes developers make is mutating the state instead of updating it immutably.
Bad:
todos.push("New Task");
setTodos(todos);
Good:
setTodos([...todos, "New Task"]);
React depends on immutability to detect changes — so always return a new object or array when updating state.
Best Practices for useState (2025 Edition)
Use one state per logical concern
const [name, setName] = useState("");
const [email, setEmail] = useState("");
Initialize with the correct type
// TypeScript example:
const [count, setCount] = useState<number>(0);
const [user, setUser] = useState<User | null>(null);
Use updater functions when working with previous state
setCount(prev => prev + 1);
Avoid nesting state updates
If your component’s logic is getting complicated, consider switching to useReducer()
— it’s more predictable for complex workflows.
When to Use useState
The useState
hook is perfect for:
- Form inputs (text, checkbox, radio)
- Modal visibility (open/close state)
- Tabs and toggles
- Count and rating systems
- Theme or language toggles (for small apps)
But it’s not ideal for:
- Shared state across multiple components
- Complex data logic with multiple dependencies
- Global state like auth, roles, or large forms
In those cases, consider:
- Context API for simple global state
useReducer()
for complex logic- State management libraries like Zustand or Redux for large-scale apps
Real-World Example: Managing Form State
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} />
<input value={password} onChange={e => setPassword(e.target.value)} />
<button type="submit">Login</button>
</form>
);
}
Clean, reactive, and easy to extend.
Common useState Pitfalls (and Fixes)
Mistake | Fix |
---|---|
Direct mutation of arrays/objects | Use spread syntax or functional updates |
Ignoring previous state dependency | Use updater function: setCount(prev => …) |
Using one large object for all state | Split into multiple useState() calls |
Trying to share state with siblings | Use Context or lift state to parent |
Summary
The useState()
hook is the foundation of interactivity in React’s modern, functional approach. It allows your components to respond to users, change dynamically, and keep the UI in sync with your data.
What you’ve learned:
- The syntax and signature of
useState
- How to initialize and update state correctly
- Examples for counters, form fields, booleans, and lists
- How to avoid common mistakes
- When to use
useState
, and when not to