State and the useState Hook
State is one of the most important concepts in React. While props allow data to be passed into a component from the outside, state is data that a component manages internally. When a component's state changes, React automatically re-renders that component to show the updated information.
Think of state as a component's memory. A counter remembers its current count. A form remembers what has been typed. A toggle switch remembers whether it is on or off. All of these are examples of state.
Introducing the useState Hook
In functional components, state is managed using the useState Hook. A Hook is a special function provided by React that adds functionality to functional components. useState is the most commonly used Hook.
To use useState, it must first be imported from React:
import { useState } from 'react';
How useState Works
useState is called with an initial value and returns an array with two items:
- The current state value
- A function to update that state value
const [count, setCount] = useState(0);
In this example:
count— the current value of the state (starts at0)setCount— the function used to update the value0— the initial state value (the starting value when the component first loads)
The naming convention is to name the state variable and its setter function as a pair: [value, setValue]. This is a common pattern in React development.
A Simple Counter Example
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Current count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(count - 1)}>Decrease</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
When the "Increase" button is clicked, setCount(count + 1) is called. React updates the state and re-renders the component, displaying the new count value. The UI is always in sync with the state.
State is Specific to Each Component Instance
Each instance of a component has its own independent state. If the same Counter component is used twice on a page, each counter tracks its own count separately:
function App() {
return (
<div>
<Counter />
<Counter />
</div>
);
}
Clicking "Increase" on the first counter does not affect the second counter. They are completely independent.
State With Strings
State can hold any type of value — not just numbers. Here is an example using a string:
import { useState } from 'react';
function NameDisplay() {
const [name, setName] = useState("Guest");
return (
<div>
<p>Hello, {name}!</p>
<button onClick={() => setName("Alice")}>Switch to Alice</button>
<button onClick={() => setName("Bob")}>Switch to Bob</button>
</div>
);
}
When a button is clicked, the name in state is updated, and the greeting automatically reflects the new name.
State With Booleans — Toggle Example
import { useState } from 'react';
function LightSwitch() {
const [isOn, setIsOn] = useState(false);
return (
<div>
<p>The light is {isOn ? "ON" : "OFF"}.</p>
<button onClick={() => setIsOn(!isOn)}>Toggle Light</button>
</div>
);
}
The ! operator flips the boolean value. Each time the button is clicked, isOn switches between true and false.
State With Objects
State can also hold an object. When updating object state, the entire object must be spread and modified — never mutate the original state directly:
import { useState } from 'react';
function UserForm() {
const [user, setUser] = useState({ name: "Alice", age: 25 });
function incrementAge() {
setUser({ ...user, age: user.age + 1 });
}
return (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
<button onClick={incrementAge}>Increase Age</button>
</div>
);
}
The spread operator { ...user } copies all existing properties, and then age: user.age + 1 overrides just the age. This keeps the rest of the object intact.
Why Not Modify State Directly?
Directly modifying a state variable (e.g., count = count + 1) without using the setter function will not trigger a re-render. React only detects state changes when the setter function is used. Direct mutations bypass React's tracking system and lead to bugs where the UI does not reflect the actual data.
Using the Previous State Value
When the new state depends on the previous state, a function should be passed to the setter instead of a direct value. This ensures the update is based on the most recent state:
// ✅ Safer approach — using previous state
<button onClick={() => setCount(prevCount => prevCount + 1)}>
Increase
</button>
This is especially important when state updates might happen multiple times in quick succession, such as during rapid clicking.
Key Points
- State is a component's internal memory — it holds data that can change over time.
- The
useStateHook is used to add state to functional components. useStatereturns the current state value and a setter function as an array.- When state is updated via the setter function, React re-renders the component.
- Each component instance has its own independent state.
- State can hold any JavaScript value — numbers, strings, booleans, arrays, or objects.
- Never modify state directly — always use the setter function provided by
useState.
The next topic covers Event Handling in React — how to respond to user actions like clicks, keystrokes, and form submissions.
