EN/CH Mode
BRUCE_FE React Interview Notes - How to Choose State Management Solutions
In-depth analysis of React state management solution selection strategies, comparing useState, useContext, Redux, Zustand, and more. Learn best practices and choose the right state management tool based on your project needs.
EN/CH Mode

Lazy to read articles? Then watch videos!
What is State Management? Why Do We Need Different Solutions?
State management refers to how we organize, store, and manage data in applications. In React applications, UI is a function of state: UI = f(state). As applications grow, state management complexity increases accordingly.
Applications of different scales and complexities require different state management solutions, mainly considering the following factors:
- 1. State Scope - Local state vs Global shared state vs Atomic state
- 2. Team Size - Small team vs Large team collaboration
- 3. Application Complexity - Simple forms vs Complex state management
Comparison of Popular State Management Solutions
Classification by State Scope
| Type | Solution | Use Cases |
|---|---|---|
| Local State | useState | Form input control, toggle states, counters |
| useReducer | Shopping cart management, multi-step forms, game states | |
| Shared State | Context API | Theme switching, user authentication state, language settings |
| Zustand | E-commerce shopping cart, todo applications, simple data dashboards | |
| Large Applications | Redux | Enterprise management systems, social media platforms, complex e-commerce sites |
| Atomic | Jotai/Recoil | Document editors, drawing applications, complex UIs requiring fine-grained updates |
按團隊規模分類
| Team Size | Recommended Solution | Reason |
|---|---|---|
| Small Team/Personal Project | useState + useContext | Simple and intuitive, fast development, no additional dependencies |
| Zustand | Lightweight, clean API, low learning curve | |
| Jotai | Atomic state management, flexible and easy to use | |
| Medium to Large Teams | Redux Toolkit | Standardized state management, rich middleware, powerful dev tools, suitable for team collaboration |
Simple Trade-offs in State Management Solutions
Start Choosing State Management Solution
Is state used only in a single component?
Use useState / useReducer
Is it a large complex application?
Consider Redux
Use Context API / Zustand / Jotai
Complexity and Feature Comparison of State Management Solutions
State Management Solutions Quick Reference
Detailed Explanation of React's Built-in State Management Solutions
1. useState - Component Local State
The simplest state management method, used for storing and updating local state within components.
import { useState } from 'react';
function Counter() {
// Declare a state variable named "count" with initial value 0
const [count, setCount] = useState(0);
return (
<div>
<p>Current count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}Best Use Cases:
- 1. Internal State Management - UI component internal state
- 2. Complex Form State - Form validation, error handling
- 3. Global State - Application-level shared state
Tip: When multiple useState variables have logical relationships, consider using useReducer to simplify state management.
2. useReducer - Complex Local State
When component state logic is complex, useReducer provides a more structured state management approach. It's based on Redux concepts but scoped to the component level.
import { useReducer } from 'react';
// Define reducer function
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error();
}
}
function Counter() {
// Initialize state and dispatch function with reducer
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Current count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}Best Use Cases:
- 1. Complex form state management
- 2. Multiple related state variables need synchronous updates
- 3. Components with complex state change logic
- 4. Need centralized state transition logic management
3. Context API - Cross-Component State Sharing
Context API is used to share data between components at different levels, avoiding 'props drilling' issues. It's particularly suitable for sharing global information like themes and user authentication.
Context API Working Principle
ThemeProvider
Stores theme state
theme: 'dark'
toggleTheme()
Header
No Context
Content
No Context
Footer
No Context
ThemeButton
useContext
⚠️ Re-renders when Context changes
Components that re-render when Context value changes
Components unaffected by Context changes
⚠️ Performance Considerations
Context value changes will cause all components consuming that Context to re-render, not suitable for frequently changing data.
1. Parent re-renders every time count changes
2. ChildA is a direct child of Parent
3. So even without using context, the render function is still called again
Optimization Tips:
- Split state into multiple Contexts to reduce unnecessary re-renders
- Use React.memo to prevent unnecessary re-renders
- Use useMemo to cache Context values and avoid unnecessary Provider re-renders
const CounterContext = createContext();
function Parent() {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
<ChildA />
<ChildB />
</CounterContext.Provider>
);
}
function ChildA() {
console.log('ChildA render');
return <p>Hello</p>;
}
function ChildB() {
const { count } = useContext(CounterContext);
console.log('ChildB render');
return <p>Count: {count}</p>;
}
// ------------------------------
const ChildA = React.memo(() => {
console.log('ChildA render');
return <p>Hello</p>;
});
// ------------------------------
const ThemeContext = createContext();
const UserContext = createContext();
function App() {
return (
<ThemeContext.Provider value={{ theme: 'dark' }}>
<UserContext.Provider value={{ user: 'John' }}>
<Main />
</UserContext.Provider>
</ThemeContext.Provider>
);
}Best Use Cases:
- 1. Application theme settings
- 2. User authentication information
- 3. Language/internationalization configuration
- 4. Global state for small to medium applications
Note: Context is not suitable for frequently changing data, because the change of Context value will cause all components that consume the Context to re-render. You can combine useMemo, memo, etc. to optimize performance.
Detailed Explanation of Third-Party State Management Libraries
1. Redux - Mature Centralized State Management
Redux is the most mature state management library in the React ecosystem, based on the principles of a single data source and immutable data, making state changes predictable and traceable.
Redux Workflow Diagram
UI
Action (State Change)
Store (Global Shared State)
Reducer (State Change Logic)
Unidirectional Data Flow: UI → Action → Reducer → Store → UI
// Create slice (Redux Toolkit)
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
}
}
});
// Export actions
export const { increment, decrement } = counterSlice.actions;
// Configure store
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// Use in React component
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './counterSlice';
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
}優勢:
- 1. High state predictability, easy to debug
- 2. Powerful development tools support time-travel debugging
- 3. Middleware system extends functionality (async operations etc.)
- 4. Mature ecosystem and best practices
適用場景:
- 1. Large e-commerce platforms (like Shopee, PChome) need to manage complex states like products, shopping carts, users, orders, etc.
- 2. Enterprise management systems (like ERP, CRM systems) handle large amounts of forms and data flow
- 3. Social media applications (like Facebook, Instagram clones) manage user interactions, notifications, and content flow
- 4. Financial trading platforms need strict state tracking and audit functionality
2. Zustand - Simple and Easy-to-Use State Management
Zustand is a small and easy-to-use state management library that provides Redux-like functionality but with a cleaner API and less boilerplate code.
Zustand Working Principle
state
{ count: 0 }
actions
increment()
decrement()
Component A
useStore()
Only subscribes to count
Component B
useStore()
Only subscribes to increment
Component C
useStore()
Subscribes to entire store
Central storage for all states and operations
Components can selectively subscribe to needed states, only re-rendering when subscribed states change
No Provider wrapper needed, direct store access
Zustand Advantages:
- 1. Clean API, almost no boilerplate code
- 2. Small package size (about 3KB)
- 3. No Provider wrapper needed
- 4. Based on hooks, integrates well with React
- 5. Supports selective subscription, built-in performance optimization
Use Cases:
- 1. Personal blogs or portfolio websites that need simple global state but don't want Redux's complexity
- 2. Small to medium e-commerce websites (like small brand websites) managing shopping carts and user preferences
- 3. Project management tools (like Trello clones) managing boards and task states
- 4. MVP products for startups, need rapid development while keeping code clean
3. Jotai/Recoil - Atomic State Management
Jotai and Recoil provide atomic state management, breaking down state into small, independent atomic units that can be composed and derived, suitable for performance-focused applications.
Atomic State Management and Component Relationship Diagram
Atomic state management breaks down state into independent small units (atoms), multiple components can share the same atom:
Atom Layer (Atoms)
Count Atom
Value: 0
Theme Atom
Value: 'dark'
User Atom
Value: { name: 'User' }
Component Layer (Components)
Counter Component
Uses count atom
Uses theme atom
Settings Component
Uses theme atom
Uses user atom
User Profile Component
Uses user atom
Rendering Behavior During State Updates
Count Atom Update
Value: 0 → 1
Theme Atom Update
Value: 'dark' → 'light'
User Atom Update
Value: Update username
Counter Component
✓ Re-renders
Settings Component
✗ No render
User Profile Component
✗ No render
Counter Component
✓ Re-renders
Settings Component
✓ Re-renders
User Profile Component
✗ No render
Counter Component
✗ No render
Settings Component
✓ Re-renders
User Profile Component
✓ Re-renders
Precise rendering in atomic state management: Only components subscribed to specific atoms will re-render when those atoms update
Advantages of Atomic State Management:
- 1. Precise updates: Only components subscribed to specific atoms will re-render, avoiding unnecessary renders
- 2. Shared state: Multiple components can share the same atom without prop drilling
- 3. State isolation: Unrelated state changes won't trigger unnecessary re-renders
- 4. Composable state: Can derive complex states from basic atoms
- 5. Testability: Each atom can be tested independently without mocking the entire state tree
Compared to traditional global state management, atomic state management is more flexible and precise, especially suitable for complex applications with frequent UI state changes.
// Jotai example
import { atom, useAtom } from 'jotai';
// Create atomic states
const countAtom = atom(0);
const doubleCountAtom = atom(get => get(countAtom) * 2);
function Counter() {
const [count, setCount] = useAtom(countAtom);
const [doubleCount] = useAtom(doubleCountAtom);
return (
<div>
<p>Count: {count}</p>
<p>Double count: {doubleCount}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}優勢:
- 1. Fine-grained re-render control, performance optimization
- 2. Composable state fragments
- 3. Derived state and async support
- 4. Code splitting friendly
- 5. Atomic-level state for easy testing and maintenance
適用場景:
- 1. Data visualization dashboards (like Google Analytics clones) that need efficient updates of multiple independent charts
- 2. Collaborative editing tools (like Google Docs or Notion clones) that need precise UI update control
- 3. Real-time chat applications (like Slack or Discord clones) that need to handle multiple chat rooms and notification states
- 4. Stock trading or cryptocurrency monitoring applications that need high-frequency updates of multiple independent data points
🔥 Common Interview Questions
(1) What are the pros and cons of different React state management solutions? How to choose the right one?
Answer: Let's briefly compare various React state management solutions:
Comparison of State Management Solutions
useState/useReducer
✅ Simple and intuitive, built-in feature
⛔️ Limited to component or requires lifting state
Use for: Forms, counters, and simple UIs
Context API
✅ Avoids props drilling, built-in feature
⛔️ All consumers re-render, performance issues
Use for: Theme, user authentication, low-frequency updates
Redux
✅ Centralized management, high predictability, powerful tools
⛔️ Lots of boilerplate, steep learning curve
// Typical Redux flow
dispatch(action) → reducer → store update → UI re-renderZustand
✅ Simple API, small bundle, good performance
⛔️ Ecosystem not as mature as Redux
// Zustand simple API
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({count: state.count + 1}))
}))Jotai/Recoil
✅ Atomic state, fine-grained updates
⛔️ Relatively new, fewer best practices
// Jotai atomic state
const countAtom = atom(0)
const doubleAtom = atom(get => get(countAtom) * 2)How to choose? Consider these questions:
- 1Application scale? Small scale use useState/Context, medium scale consider Zustand/Jotai, large scale consider Redux.
- 2State complexity? Simple values use useState or useReducer, complex objects use third-party libraries
- 3Update frequency? High-frequency updates avoid using Context (affects everything), consider Zustand/Jotai
- 4Team familiarity? Choose technologies familiar to the team to reduce learning costs
- 5Debugging needs? Need complex global state sharing choose Redux
Interview tip: Don't just say "X is the best"—show that you understand the trade-offs of each solution and can choose the right tool for the specific need.