State
Understand React state — the internal memory of a component that drives UI updates. Learn when to use local state, how to structure it, and the patterns senior engineers use to avoid the most common state bugs.
Last reviewed: January 2025
🔍 What Is It?
State is data that:
- Changes over time — user interactions, API responses, timers
- Belongs to a specific component — not shared globally by default
- Triggers a re-render when updated — React re-runs your component function
- Persists between re-renders — unlike regular variables which reset on every render
In function components, state is managed with the useState hook. For complex state logic, useReducer is used. State can be:
- →Local state — owned by one component (form inputs, toggle open/closed, loading flags)
- →Lifted state — moved to a parent so multiple children can share it
- →Global state — managed outside React via Context, Zustand, or Redux
🎯 Why Do We Need It?
Without state, React components would be static HTML. State is what makes UIs interactive:
- →A button that shows a loading spinner while an API call is in flight
- →A form that tracks what the user is typing
- →A modal that opens and closes
- →A table that shows different data based on the selected filter
- →A counter that increments on click
Every interactive element in your app is driven by state somewhere. The question is not whether to use state — it is where to put it and how to structure it.
🏗️ What Happens in Real Projects?
In a real healthcare portal:
- →Form state — patient registration form tracks name, DOB, insurance ID as the user types
- →UI state — sidebar open/closed, modal visible, active tab
- →Server state — patient records fetched from API (often managed by React Query)
- →Filter state — selected date range, status filter, search term in a claims list
- →Optimistic state — immediately show a status change while the API call is in flight
Most components only need 1–3 state variables. If a component has 10+ useState calls, it is doing too much and should be split.
⚙️ How It Works
useState mechanics:
const [count, setCount] = useState(0)— declares state with initial value 0countis the current value — read it in JSX and logicsetCount(5)— schedules a re-render with count = 5setCount(prev => prev + 1)— functional update, safe for async contexts- React batches multiple setState calls in event handlers (React 18: batches everywhere)
State is a snapshot:
Each render captures a snapshot of state. If you call setCount(count + 1) three times in a row, count is the same value in all three calls — you get count + 1, not count + 3. Use functional updates to avoid this.
State initialisation:
For expensive initial values, use lazy initialisation: useState(() => computeExpensiveValue()) — the function runs only once.
💻 Example
State Patterns in Production — From simple toggles to async state with loading and error handling.
🤔 Common Confusions
❓ Why does my state not update immediately after calling setState?
setState is asynchronous — it schedules a re-render but does not immediately change the variable. If you log state right after setState, you see the old value. The new value is available in the next render. For logic that depends on the new value, use the functional update form: setState(prev => prev + 1).
❓ Why do I lose state when a component unmounts?
State is tied to a component's lifecycle. When a component unmounts, its state is destroyed. When it remounts, state starts fresh from the initial value. If you need to persist state across mounts (e.g., form data across navigation), store it in a parent component, URL params, or localStorage.
❓ Should I put everything in state?
No. Only put data in state if it needs to trigger a re-render when it changes. Derived values (computed from state) should be calculated inline or with useMemo — not stored as separate state. Refs (useRef) are for values that change but should not trigger re-renders.
🚨 Common Production Issues
**Bug: Stale closure** — An event handler or useEffect captures the initial state value and never sees updates. Fix: use the functional update form setState(prev => prev + 1) or add the state variable to the useEffect dependency array.
**Bug: Object state mutation** — setState({ ...form, name: value }) is correct. form.name = value is not — mutating state directly does not trigger a re-render and causes subtle bugs.
**Bug: Race condition in async state** — Two API calls in flight; the slower one resolves last and overwrites the faster one's result. Fix: use a cancellation flag (let cancelled = false) in useEffect cleanup.
**Performance: Too many useState calls** — 10+ individual state variables that always update together should be combined into a single object with useReducer for clarity and performance.
✅ Best Practices
Group related state into objects — not 5 separate useState calls for one form
Use functional updates setState(prev => ...) when new state depends on old state
Initialise state with the correct type — never start with null if you can avoid it
Lift state to the lowest common ancestor that needs it — not higher
Derive values from state inline rather than creating redundant state variables
Use useReducer for state with multiple sub-values or complex update logic
Always handle loading, data, and error states for async operations
🏛️ Architect's Note
State management is where React architecture decisions get hard. The question is always: where does this state live?
The answer follows a hierarchy:
- Local state — default choice, keep it in the component that owns it
- Lifted state — when two siblings need to share it, move to their parent
- Context — when many components across the tree need it (theme, auth user)
- Zustand/Redux — when state is complex, shared widely, or needs devtools
The biggest mistake teams make is jumping straight to Redux for everything. Start local, lift when needed, reach for a library only when lifting becomes painful. Most apps need much less global state than developers assume.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
React Technology Guide
Return to the React learning hub.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
Sources
Sources / References
Use these React references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide React learning path
Browse the full React topic collection and learning order on SplashRide.
React Interview Prep
Practice React interview questions and scenario-based answers.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
useState
Master useState — the most fundamental React hook. Learn how it works under the hood, the patterns senior engineers use, and the bugs that trip up even experienced developers.
useReducer
Learn when useReducer is a better fit than useState for React screens with complex transitions, event-driven state, and safer update logic.