React useState Hook
State is data that a component remembers and that can change over time. The useState hook adds state to a function component.
What is a Hook?
Hooks are special functions whose names start with use. They let function components use React features such as state. You import them from react.
Two rules for all hooks:
- Call hooks only at the top level of a component, not inside
if, loops or nested functions - Call hooks only inside React components or your own custom hooks
Your First State
Example
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Cups of chai today: {count}</p>
<button onClick={() => setCount(count + 1)}>Add one</button>
</div>
);
}Cups of chai today: 0 (after 3 clicks) Cups of chai today: 3
useState(0) returns an array with two things, which we destructure:
| Part | Meaning |
|---|---|
count | The current value (starts at 0) |
setCount | A function to change the value |
0 | The initial value, used only on the first render |
Why Not a Normal Variable?
A normal variable is reset every time the component runs, and changing it does not tell React to update the screen. Calling the setter (setCount) does two things: it stores the new value and asks React to re-render the component with it.
State Updates Are Not Immediate
After you call the setter, the variable keeps its old value until the next render.
Example
function handleClick() {
setCount(count + 1);
console.log(count); // still the old value
}If the new value depends on the old one, and you update more than once, use the updater function form:
Example
function addThree() {
setCount((c) => c + 1);
setCount((c) => c + 1);
setCount((c) => c + 1);
}Each updater receives the latest value, so the count really goes up by 3. With setCount(count + 1) three times, it would go up by only 1.
Several State Variables
A component can have as many state variables as it needs:
Example
function LightSwitch() {
const [isOn, setIsOn] = useState(false);
const [room, setRoom] = useState("Bedroom");
return (
<div>
<p>{room} light is {isOn ? "ON" : "OFF"}</p>
<button onClick={() => setIsOn(!isOn)}>Toggle</button>
<button onClick={() => setRoom("Kitchen")}>Go to kitchen</button>
</div>
);
}Objects in State
Never change a state object directly. Create a new object with the spread operator ... and change only what you need.
Example
function Profile() {
const [student, setStudent] = useState({ name: "Ali", city: "Karachi", age: 19 });
function birthday() {
setStudent({ ...student, age: student.age + 1 });
}
return (
<div>
<p>{student.name} from {student.city} is {student.age}</p>
<button onClick={birthday}>Happy birthday!</button>
</div>
);
}Ali from Karachi is 19 (after click) Ali from Karachi is 20
Arrays in State
The same idea applies to arrays. Use methods that return a new array.
| Task | Write |
|---|---|
| Add an item | setItems([...items, newItem]) |
| Remove an item | setItems(items.filter((i) => i.id !== id)) |
| Change an item | setItems(items.map((i) => (i.id === id ? { ...i, done: true } : i))) |
Avoid push, splice or assigning items[0] = .... These change the old array, and React may not notice.
Example
function TaskList() {
const [tasks, setTasks] = useState(["Revise CSS"]);
return (
<div>
<button onClick={() => setTasks([...tasks, "Practise JavaScript"])}>Add task</button>
<ul>
{tasks.map((t) => (
<li key={t}>{t}</li>
))}
</ul>
</div>
);
}If two sibling components need the same state, move the state up to their common parent and pass it down as props. This is called lifting state up.
Exercise
Exercise
What does calling setCount(5) do?
