3/8/2026 | react | javascript | frontend | web development | react hooks
React's Two Most Important Hooks: useState vs useEffect

React's Two Most Important Hooks: useState vs useEffect
If React components were living creatures, they would need two things to function:
- Memory π§
- The ability to interact with the outside world π
In React, these powers come from two hooks:
useState
useEffect
Mastering them is the moment React suddenly clicks.
This article will show:
- what they do
- when to use them
- how they work together
- mistakes beginners make
1οΈ. useState β The Memory of Your Component
Every React component needs memory.
For example:
- a counter needs to remember its value
- a form needs to remember what the user typed
- a modal needs to remember whether it is open
That memory lives in state.
React gives us state using the useState hook.
Basic Example
import { useState } from "react";
function Counter() {
const [count, setCount] = useState<number>(0);
const increment = () => {
setCount(prev => prev + 1);
};
return (
<button onClick={increment}>
Count: {count}
</button>
);
}
Whatβs happening here?
| Part | Meaning |
|---|---|
count | The current value |
setCount | Function to update the value |
useState(0) | Initial state |
Whenever setCount runs, React:
- updates the state
- re-renders the component
- updates the UI
Where useState is Used in Real Apps
You will use it everywhere.
Form Inputs
const [username, setUsername] = useState("");
Toggle UI Elements
const [isOpen, setIsOpen] = useState(false);
API Data Storage
const [posts, setPosts] = useState([]);
Think of useState as:
The variable that React remembers between renders
2οΈ. useEffect β React's Portal to the Outside World
React components are supposed to be pure functions.
But real apps need to do things like:
- fetch data
- start timers
- subscribe to events
- update the document title
These are called side effects.
And React handles them using:
useEffect
A Simple Example
import { useEffect } from "react";
useEffect(() => {
console.log("Component mounted!");
}, []);
This runs once when the component loads.
Why?
Because of the dependency array.
The Dependency Array (The Secret Sauce)
The second argument controls when the effect runs.
Run Once
useEffect(() => {
fetchPosts();
}, []);
Runs when the component first mounts.
Common uses:
- API calls
- loading data
- initializing things
Run When Something Changes
useEffect(() => {
console.log("Count changed");
}, [count]);
Runs whenever count updates.
Run Every Render
useEffect(() => {
console.log("Rendered");
});
This runs after every render.
β οΈ Usually you do not want this.
A Real Example: Fetching Data
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch("/api/posts")
.then(res => res.json())
.then(data => setPosts(data));
}, []);
Flow of events:
Component mounts
β
useEffect runs
β
API request happens
β
State updates
β
UI re-renders with data
Cleanup Functions (A Hidden Superpower)
Sometimes effects create ongoing processes, like:
- timers
- event listeners
- subscriptions
These must be cleaned up.
Example:
useEffect(() => {
const interval = setInterval(() => {
console.log("Tick");
}, 1000);
return () => clearInterval(interval);
}, []);
When the component unmounts, React runs the cleanup function.
Without cleanup, your app can leak memory.
When to Use Each Hook
| Situation | Hook | Reason |
|---|---|---|
| Form input | useState | Track what user typed |
| Toggle menu | useState | UI state |
| Fetch API data | useEffect | Side effect |
| Timer | useEffect | Uses interval |
| Search feature | Both | State stores query, effect triggers fetch |
When They Work Together (The Real Magic)
Most real features use both hooks together.
Example: Auto-saving a blog editor
const [content, setContent] = useState("");
useEffect(() => {
const interval = setInterval(() => {
saveToDatabase(content);
}, 30000);
return () => clearInterval(interval);
}, [content]);
What happens here?
User types
β
State updates
β
Effect notices change
β
Auto-save triggers
Common Beginner Mistakes
Infinite Loops
useEffect(() => {
setCount(count + 1);
}, [count]);
This causes React to re-render forever.
Missing Dependencies
Bad:
useEffect(() => {
fetchUser(userId);
}, []);
Correct:
useEffect(() => {
fetchUser(userId);
}, [userId]);
Overusing useEffect
Not everything needs an effect.
Rule:
If it can run during render, it should not be inside useEffect.
The Mental Model That Makes React Click
Think of it like this:
useState = memory
useEffect = reactions
Or visually:
State changes
β
React renders UI
β
Effects run afterwards
Quick Comparison
| Feature | useState | useEffect |
|---|---|---|
| Purpose | Store data | Run side effects |
| Causes render | Yes | No |
| Runs when | State updates | After render |
| Examples | forms, toggles | API calls, timers |
Final Thoughts
Learning React often feels confusing at first.
But once you understand these two ideas:
State stores data
Effects react to changes
Everything starts to make sense.
Every React application β from tiny widgets to massive dashboards β is built on top of these two hooks working together.
Master them, and you're no longer just using React.
You're thinking in React.