State and Events
Course: React JS & Tailwind CSS - Full Course
Introduction
Up until now, our components have been pretty static. We’ve hardcoded data or passed props in, but nothing has really changed once the component was rendered.
In real apps, we need components to change over time. A task tracker gets marked complete, a form updates as you type, or a button triggers some action. This is where state and events come in.
What is State?
Think of state as a component’s own “memory.” It’s data that belongs to a specific component and can change over time.
For example, a todo item might have a state that says whether it’s completed or not. When the state changes, React will automatically re-render the component to reflect the new value.
To create state, we use the useState
hook that React provides. Hooks are just special functions, and useState
is one you’ll use all the time.
Here's an example of how we can implement state in a Counter function, which will store the count:
import { useState } from "react"
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
</div>
)
}
Something to keep in mind is that we'll need to import the useState
hook which is a function that is built into and provided by React.
When creating state above, you'll notice that we declared count and setCount. count
represents the current value of count and setCount
is a special function used to update that value. useState(0)
tells React that the starting value is 0.
Right now, we're not doing anything to count just yet, we're simply rendering it. To make changes to count based on user input, we need to listen to "Events".
What are Events?
Events are how we respond to user actions, like clicks, typing, or form submissions. In React, you attach event handlers directly to your JSX using camelCase, like onClick
, onChange
, or onSubmit
.
When an event happens, we usually update state to reflect that change, which in turn updates what the user sees on the screen.
State + Event
Here’s how they work together in practice:
- You declare some state with
useState
. - You display that state in your JSX.
- You attach an event handler (like
onClick
) that updates the state. - React re-renders the component with the new state.
Let's revisit the Counter function as an example:
import { useState } from "react"
function Counter() {
const [count, setCount] = useState(0)
const handleIncrement = () => {
setCount(count + 1)
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increase Count</button>
</div>
)
}
Breaking down what's going on. After creating state to hold the count, we declare a function handleIncrement
that takes the current count and increases it by 1.
Note that we are using the setter function, setCount to do this and not count = count + 1
. We want to use setCount
because that tells React we're changing state, and for React to update state and re-render the component.
Things to Be Aware of with State
State Updates are Asynchronous
When you call the state updater function (like setCount), React doesn’t change the state immediately, it schedules an update. This means you shouldn’t rely on the state changing instantly within the same function call.
This can be seen if you add a console log to the handleIncrement function:
const handleIncrement = () => {
setCount(count + 1)
console.log(count)
}
You'll see in the console that the old count is logged.
State is Local to the Component
Each component manages its own state. If multiple components need the same data, you’ll eventually “lift state up” to a common parent. (We’ll cover this later!)
Just know for now, if you render multiple Counter components, they will each have their own state. Give it a try:
<div>
<Counter />
<Counter />
</div>
Test Yourself
Now that you know how state is implemented, try to implement state by storing the status of the taks into the TaskItem
component we've been working on!