Start with useState, and mean it
Most components need nothing more complicated than useState. A toggle, a form field, a loading flag - these don't need a library, they need one line.
const [isOpen, setIsOpen] = useState(false);
const [email, setEmail] = useState("");
When state gets tangled, reach for useReducer
Once you notice several related pieces of state updating together - say, a form with loading, error, and data states that all change in response to the same events - a reducer keeps the update logic in one predictable place instead of scattered across several setX calls.
function formReducer(state, action) {
switch (action.type) {
case "SUBMIT_START":
return { ...state, loading: true, error: null };
case "SUBMIT_SUCCESS":
return { ...state, loading: false, data: action.payload };
case "SUBMIT_ERROR":
return { ...state, loading: false, error: action.error };
default:
return state;
}
}
const [state, dispatch] = useReducer(formReducer, {
loading: false,
error: null,
data: null,
});
Lifting state up before reaching for context
If two sibling components need to share a value, the simplest fix is usually moving that state up to their common parent and passing it down as props - no extra library, no extra abstraction.
Context is for rarely-changing, widely-needed values
Theme, the logged-in user, locale - things that barely change but are needed almost everywhere - are a good fit for useContext. Context re-renders every consumer on every update though, so it's a poor fit for anything that changes frequently, like form input on every keystroke.
Only then, consider a library
Reach for something like Zustand or Redux once state genuinely needs to be shared across many unrelated parts of the app, or the update logic has grown complex enough that a dedicated store actually simplifies things rather than adding ceremony.
Comments
Loading comments...
Related Articles
Taming Slow MongoDB Aggregation Pipelines in Production
An aggregation pipeline that returns instantly on your 500-document local dataset can crawl once it hits a few million real documents. Here's how to actually find and fix the stage that's costing you.
Read ArticleBuilding a Sliding-Window Rate Limiter for Node.js APIs with Redis
Fixed-window rate limiting looks fine in a demo and then lets through double the traffic right at the window boundary. Here's the sliding-window-counter approach that fixes that, built directly on Redis.
Read ArticleMulti-Tenant Data Isolation: Shared Schema vs Separate Databases
Every multi-tenant SaaS eventually has to answer one question honestly: how do you stop one customer from ever seeing another customer's data? Here's how the three common isolation strategies actually compare in practice.
Read Article