Learn how to add interactivity to your React components using state with the useState hook.
Welcome to one of the most important concepts in React! Up until now, our components have been pretty static - they display information but don't really change. State is what brings your components to life by making them interactive and dynamic.
Think of state like the "memory" of your component. Just like you remember your name, age, or what you had for breakfast, React components can remember information too. And when that information changes, React automatically updates what's displayed on the screen.
Let's start with a simple question: What if you wanted to build a counter that increases every time someone clicks a button?
With what we've learned so far, you might try something like this:
function Counter() {
let count = 0; // This won't work as expected!
const handleClick = () => {
count = count + 1;
console.log(count); // This will log the new number
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Click me!</button>
</div>
);
}
Try this in your head: What do you think happens when you click the button?
The answer might surprise you: The number in the console will increase, but the number on the screen will stay at 0!
Why? Because React doesn't know that something important changed. It's like having a whiteboard with a number written on it, changing the number in your head, but never erasing and rewriting the whiteboard.
React state solves this problem. When you use state, you're telling React: "Hey, this piece of data is important. When it changes, please update the screen!"
Here's the same counter, but using state correctly:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // This WILL update the screen!
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Click me!</button>
</div>
);
}
Now when you click the button, the number on screen increases! ✨
Let's break down that mysterious useState line step by step:
const [count, setCount] = useState(0);
First, we need to import useState from React:
import React, { useState } from 'react';
Think of this like getting a special tool from React's toolbox.
useState(0)
We call useState and give it the starting value. In this case, we want our counter to start at 0.
useState gives us back two things in an array:
const [count, setCount] = useState(0);
This is like saying:
count"setCount"Important: You can name these anything you want! These are all valid:
const [age, setAge] = useState(25);
const [name, setName] = useState("Alice");
const [isVisible, setIsVisible] = useState(true);
const [pizza, setPizza] = useState("margherita");
Let's build something together! We'll create a simple greeting component that lets users change their name.
Step 1: Start with the basic structure
function PersonalGreeting() {
return (
<div>
<h1>Hello, World!</h1>
<input type="text" placeholder="Enter your name" />
</div>
);
}
Step 2: Add state to remember the name
function PersonalGreeting() {
const [name, setName] = useState("World");
return (
<div>
<h1>Hello, {name}!</h1>
<input type="text" placeholder="Enter your name" />
</div>
);
}
Step 3: Connect the input to our state
function PersonalGreeting() {
const [name, setName] = useState("World");
const handleNameChange = (event) => {
const newName = event.target.value;
setName(newName);
};
return (
<div>
<h1>Hello, {name}!</h1>
<input
type="text"
placeholder="Enter your name"
value={name}
onChange={handleNameChange}
/>
</div>
);
}
What's happening here?
handleNameChange runsevent.target.valuesetName with the new valueState isn't just for numbers and text. You can store different types of information:
function ScoreTracker() {
const [score, setScore] = useState(0);
return (
<div>
<h2>Score: {score}</h2>
<button onClick={() => setScore(score + 10)}>
+10 points
</button>
</div>
);
}
function LightSwitch() {
const [isOn, setIsOn] = useState(false);
return (
<div>
<h2>Light is {isOn ? "ON" : "OFF"}</h2>
<button onClick={() => setIsOn(!isOn)}>
Toggle Light
</button>
</div>
);
}
function FruitList() {
const [fruits, setFruits] = useState(["Apple", "Banana"]);
const addOrange = () => {
setFruits([...fruits, "Orange"]);
};
return (
<div>
<h2>My Fruits:</h2>
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
<button onClick={addOrange}>Add Orange</button>
</div>
);
}
Let's build something more realistic - a single todo item that can be marked as complete:
function TodoItem() {
const [task, setTask] = useState("Learn React State");
const [isCompleted, setIsCompleted] = useState(false);
const toggleComplete = () => {
setIsCompleted(!isCompleted);
};
return (
<div style={{
padding: "10px",
backgroundColor: isCompleted ? "#d4edda" : "#fff3cd",
border: "1px solid #ccc",
borderRadius: "5px"
}}>
<h3 style={{
textDecoration: isCompleted ? "line-through" : "none"
}}>
{task}
</h3>
<button onClick={toggleComplete}>
{isCompleted ? "Mark Incomplete" : "Mark Complete"}
</button>
<p>
Status: {isCompleted ? "✅ Done!" : "⏳ In Progress"}
</p>
</div>
);
}
What makes this interesting?
// ❌ DON'T do this:
count = count + 1;
// ✅ DO this instead:
setCount(count + 1);
Why? React needs to know when things change so it can update the screen. If you change state directly, React doesn't know anything happened!
This means the change doesn't happen immediately:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // This might still show the old value!
};
return <button onClick={handleClick}>Count: {count}</button>;
}
If your new state depends on the old state, use a function:
// ✅ Better way:
const handleClick = () => {
setCount(previousCount => previousCount + 1);
};
Try building this component yourself:
Requirements:
Hint: You'll need state that stores the current mood as a string.
Solution (try it yourself first!):
function MoodTracker() {
const [mood, setMood] = useState("neutral");
const getMoodEmoji = () => {
switch(mood) {
case "happy": return "😊";
case "sad": return "😢";
case "excited": return "🤩";
default: return "😐";
}
};
const getMoodMessage = () => {
switch(mood) {
case "happy": return "You're feeling great!";
case "sad": return "Hope your day gets better!";
case "excited": return "You're full of energy!";
default: return "You're feeling neutral.";
}
};
return (
<div style={{ textAlign: "center", padding: "20px" }}>
<h2>How are you feeling?</h2>
<div style={{ fontSize: "60px", margin: "20px" }}>
{getMoodEmoji()}
</div>
<p style={{ fontSize: "18px", margin: "20px" }}>
{getMoodMessage()}
</p>
<div>
<button onClick={() => setMood("happy")}>Happy</button>
<button onClick={() => setMood("sad")}>Sad</button>
<button onClick={() => setMood("excited")}>Excited</button>
<button onClick={() => setMood("neutral")}>Neutral</button>
</div>
</div>
);
}
Congratulations! You now understand:
✅ What state is - the "memory" of your component
✅ Why we need state - to make components interactive
✅ How to use useState - the hook that adds state to components
✅ Different types of state - numbers, text, booleans, arrays
✅ State rules - never modify directly, always use the setter function
// ❌ This will cause an error:
function Counter() {
const [count, setCount] = useState(0); // useState is not defined!
}
// ✅ Always import it:
import React, { useState } from 'react';
// ❌ Wrong:
<button onClick={setCount(count + 1)}>+</button>
// ✅ Correct:
<button onClick={() => setCount(count + 1)}>+</button>
// ❌ This won't work as expected:
const handleClick = () => {
setCount(count + 1);
console.log(count); // Still shows old value
};
// ✅ Use useEffect if you need to do something after state changes
In our next lesson, we'll learn about Event Handling - how to respond to user clicks, form inputs, and other interactions. You'll discover how to make your components respond to user actions in more sophisticated ways.
State and events work hand-in-hand to create truly interactive user interfaces!
Before moving on, try to answer these questions:
Answers: 1) useState, 2) Current value and setter function, 3) Using the setter function, 4) React won't know to re-render the component