1. Introduction đ
State is what makes React applications interactive. While props let components receive data from their parents, state lets a component remember information and update its own UI over time â in response to user input, network responses, or timers.
Information
2. What is State? đ¤
State is data that a component owns and can change over time. When state changes, React automatically re-renders the component to reflect the new data on screen.
Code Snippet
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}3. Why State? đĄ
Without state, components could only render static output based on their props. State enables:
- Interactivity: Responding to clicks, input, and other user actions.
- Dynamic UI: Reflecting real-time data like form inputs or loading indicators.
- Persistence Across Renders: Remembering values between re-renders, unlike regular variables.
- Encapsulation: Each component instance manages its own independent state.
4. State vs Props âī¸
| Aspect | State | Props |
|---|---|---|
| Owner | Owned by the component itself | Passed in from a parent component |
| Mutability | Can change via setter functions | Read-only from the child's perspective |
| Purpose | Tracks internal, changing data | Configures a component from outside |
| Triggers Re-render | Yes, when updated | Yes, when the parent passes new values |
5. Understanding useState đĒ
useState is the primary Hook used to add state to function components. It returns an array containing the current value and a setter function.
Code Snippet
const [state, setState] = useState(initialValue);- state â the current value, available on every render.
- setState â a function used to update the value and trigger a re-render.
- initialValue â the value used only on the first render.
6. Creating State đą
Code Snippet
import { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
return (
<form>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
</form>
);
}Tip
7. Reading State đ
The current state value is simply the first element returned by useState. It behaves like a regular variable during render.
Code Snippet
function Profile() {
const [name, setName] = useState("Guest");
return <h1>Welcome, {name}!</h1>;
}Note
8. Updating State âī¸
Calling the setter function schedules a state update and triggers a re-render of the component (and its children, where applicable).
Code Snippet
function Toggle() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(!isOn)}>
{isOn ? "ON â
" : "OFF â"}
</button>
);
}Important
9. Multiple State Variables đĸ
Components commonly use several independent useState calls to track distinct pieces of data separately.
Code Snippet
function SignupForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [agreedToTerms, setAgreedToTerms] = useState(false);
// Each variable updates independently
}Best Practice
10. Functional State Updates đ
When new state depends on the previous state, pass a function to the setter instead of a direct value. This guarantees you're working with the most up-to-date state, especially important with rapid or batched updates.
Code Snippet
function Counter() {
const [count, setCount] = useState(0);
function incrementTwice() {
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
// Correctly results in +2
}
return <button onClick={incrementTwice}>Count: {count}</button>;
}Warning
11. Initial State đ
The value passed to useState is used only once, during the component's first render. On subsequent renders, React ignores this argument and uses the current stored state instead.
Code Snippet
function Timer() {
const [seconds, setSeconds] = useState(0); // used only on first render
// ...
}12. Lazy Initialization đ¤
If computing the initial state is expensive, pass a function to useState instead of a value. React calls this function only once, on the first render.
Code Snippet
function TodoList() {
const [todos, setTodos] = useState(() => {
// Expensive operation runs only once
return loadTodosFromLocalStorage();
});
}Caution
13. Updating Objects in State đ§ą
Because state must be treated as immutable, updating an object requires creating a new object rather than mutating the existing one.
Code Snippet
function ProfileForm() {
const [user, setUser] = useState({ name: "Alice", age: 25 });
function updateAge() {
setUser({ ...user, age: user.age + 1 });
}
return <button onClick={updateAge}>Birthday đ</button>;
}Danger
14. Updating Arrays in State đ
Similarly, arrays in state must be updated immutably, using methods that return new arrays instead of mutating methods.
Code Snippet
function TodoList() {
const [todos, setTodos] = useState(["Learn React"]);
function addTodo(newTodo) {
setTodos([...todos, newTodo]); // â
new array
}
function removeTodo(index) {
setTodos(todos.filter((_, i) => i !== index)); // â
new array
}
}| Avoid (Mutating) | Prefer (Immutable) |
|---|---|
| array.push(item) | [...array, item] |
| array.pop() | array.slice(0, -1) |
| array.splice(i, 1) | array.filter((_, idx) => idx !== i) |
| array[i] = newVal | array.map((v, idx) => idx === i ? newVal : v) |
15. Nested State đĒ
Updating deeply nested state requires copying every level of the structure that changes, since immutability applies at each nested layer.
Code Snippet
function AddressForm() {
const [user, setUser] = useState({
name: "Alice",
address: { city: "Chennai", zip: "600001" },
});
function updateCity(newCity) {
setUser({
...user,
address: { ...user.address, city: newCity },
});
}
}Tip
16. Immutable State Updates đ
Immutability is a core rule of React state: never modify existing state objects or arrays directly. Always create a new copy with the desired changes.
- Enables React to detect changes efficiently via reference comparison.
- Prevents subtle, hard-to-trace bugs from unexpected mutations.
- Supports predictable debugging and features like time-travel debugging.
Best Practice
17. State Batching đ¯
React batches multiple state updates that occur within the same event handler into a single re-render, improving performance by avoiding redundant renders.
Code Snippet
function Form() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
setCount(count + 1);
setFlag(true);
// Only ONE re-render happens, not two
}
}Information
18. State Queue đĨ
When multiple state updates are triggered before a re-render, React processes them as a queue. Functional updates ensure each update in the queue receives the correctly updated previous value.
Code Snippet
function QueueExample() {
const [number, setNumber] = useState(0);
function handleClick() {
setNumber((n) => n + 1); // queued: 0 -> 1
setNumber((n) => n + 1); // queued: 1 -> 2
setNumber((n) => n + 1); // queued: 2 -> 3
}
return <button onClick={handleClick}>{number}</button>; // results in 3
}19. Asynchronous State Updates âąī¸
State updates are not applied immediately â React schedules them and applies the change before the next render, meaning the updated value isn't accessible right after calling the setter.
Code Snippet
function Example() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // logs the OLD value, not the new one
}
}Caution
20. Derived State đ§Ž
Not every value needs its own useState. If a value can be calculated from existing state or props during render, it's usually better to derive it directly instead of storing it redundantly.
Code Snippet
function Cart({ items }) {
// â Avoid: redundant state that can go out of sync
// const [total, setTotal] = useState(0);
// â
Prefer: derived directly during render
const total = items.reduce((sum, item) => sum + item.price, 0);
return <p>Total: ${total.toFixed(2)}</p>;
}Best Practice
21. Lifting State Up âŦī¸
When multiple components need to share the same state, move that state to their closest common parent, then pass it down via props.
Code Snippet
function Parent() {
const [selectedId, setSelectedId] = useState(null);
return (
<div>
<ItemList selectedId={selectedId} onSelect={setSelectedId} />
<ItemDetails selectedId={selectedId} />
</div>
);
}Tip
22. Sharing State đ
Beyond lifting state to a shared parent, larger applications may share state across distant components using the Context API or dedicated state management libraries.
| Scope | Recommended Approach |
|---|---|
| Sibling components, shared parent | Lift state up |
| Deeply nested components | Context API |
| Complex, app-wide state | State management library (Zustand, Redux) |
Reference
23. Preserving State đž
React preserves a component's state across re-renders as long as it remains at the same position in the component tree, with the same type.
Code Snippet
function App({ showCounter }) {
return (
<div>
{showCounter && <Counter />}
{/* Counter's state resets if unmounted and remounted */}
</div>
);
}Note
24. Resetting State đ
To intentionally reset a component's state (e.g., when switching between items), change its key prop â React treats a new key as an entirely new component instance.
Code Snippet
function ProfileEditor({ userId }) {
return <Form key={userId} />;
// Changing userId forces Form to fully reset its internal state
}Tip
25. State Management Patterns đī¸
Managed entirely within a single component using useState. Best for UI-only concerns like toggles, form inputs, or hover states.
Shared between a few closely related components by lifting state to their common parent. Best for moderate sharing needs.
Managed via Context or libraries like Redux or Zustand for state needed across many, unrelated parts of the app.
26. Common State Problems đ
- Stale closures: Reading state variables captured from an old render inside callbacks or effects.
- Direct mutation: Modifying objects/arrays in place instead of creating new copies.
- Too much state: Storing values that could instead be derived during render.
- State duplicated across components: Leading to components falling out of sync.
- Unnecessary re-renders: Storing frequently changing values at too high a level in the tree.
27. State Best Practices đ
- Keep state as minimal as possible â derive values instead of storing redundant copies.
- Use functional updates when new state depends on the previous value.
- Always update objects and arrays immutably.
- Lift state up only as far as necessary â avoid placing state higher than needed.
- Use the key prop deliberately to reset state when appropriate.
- Split unrelated state into separate useState calls for clarity.
28. TypeScript with State đˇ
TS can infer state types automatically from the initial value, or you can specify them explicitly using generics for more complex cases.
Code Snippet
// Inferred type: boolean
const [isVisible, setIsVisible] = useState(false);
// Explicit type for complex state
interface User {
name: string;
age: number;
}
const [user, setUser] = useState<User | null>(null);Tip
29. Frequently Asked Questions â
Question
Answer
Question
Answer
Question
Answer
30. Summary đ
State gives React components memory, enabling dynamic, interactive user interfaces. Mastering useState â including immutable updates, functional updates, and patterns like lifting state up â is essential for building predictable and maintainable applications.