By AriesZhou · · 4 min read
React State Management
React
As an application grows, state management can easily become chaotic.
React state management is central to application architecture. Good state management makes code clearer and easier to maintain.
1. React built-in state management
1.1 useState: local state
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: '张三', age: 25 });
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
<button onClick={() => setUser({ ...user, age: user.age + 1 })}>长大</button>
</div>
);
}
1.2 useReducer: complex state
function todoReducer(state, action) {
switch (action.type) {
case 'ADD':
return [...state, action.payload];
case 'TOGGLE':
return state.map(todo =>
todo.id === action.payload ? { ...todo, done: !todo.done } : todo
);
case 'DELETE':
return state.filter(todo => todo.id !== action.payload);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
return (
<div>
<button onClick={() => dispatch({ type: 'ADD', payload: { id: Date.now(), text: '新任务', done: false }})}>
添加
</button>
</div>
);
}
1.3 Context: cross-component sharing
const ThemeContext = createContext();
const UserContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState({ name: '张三', id: 1 });
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
function Layout() {
const { theme, setTheme } = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div className={theme}>
<h1>欢迎, {user.name}</h1>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
切换主题
</button>
</div>
);
}
2. State management best practices
2.1 Minimize state
// ❌ 避免:冗余状态
function BadExample() {
const [firstName, setFirstName] = useState('张');
const [lastName, setLastName] = useState('三');
const fullName = firstName + lastName; // 这不需要是状态
return <div>{fullName}</div>;
}
// ✅ 推荐:计算属性
function GoodExample() {
const [user, setUser] = useState({ firstName: '张', lastName: '三' });
const fullName = `${user.firstName}${user.lastName}`; // 通过计算得到
return <div>{fullName}</div>;
}
2.2 Lifting state up
// ❌ 避免:状态分散
function Parent() {
return (
<>
<ChildA />
<ChildB />
</>
);
}
// ✅ 推荐:提升到共同父组件
function Parent() {
const [shared, setShared] = useState('');
return (
<>
<ChildA value={shared} onChange={setShared} />
<ChildB value={shared} />
</>
);
}
2.3 Immutable data
// ❌ 避免:直接修改状态
function badReducer(state, action) {
switch (action.type) {
case 'ADD':
state.push(action.payload); // 直接修改!
return state;
}
}
// ✅ 推荐:创建新数据
function goodReducer(state, action) {
switch (action.type) {
case 'ADD':
return [...state, action.payload]; // 新数组
case 'UPDATE':
return state.map(item =>
item.id === action.payload.id ? { ...item, ...action.payload } : item
);
}
}
3. Third-party state management
3.1 Redux
// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
// Component.jsx
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './counterSlice';
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>计数: {count}</p>
<button onClick={() => dispatch(increment())}>增加</button>
</div>
);
}
3.2 Zustand (lightweight)
import { create } from 'zustand';
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));
function Counter() {
const { count, increment, decrement } = useStore();
return (
<div>
<p>计数: {count}</p>
<button onClick={decrement}>减少</button>
<button onClick={increment}>增加</button>
</div>
);
}
3.3 Solution comparison
| Solution | Pros | Cons | Use cases |
|---|---|---|---|
| useState | Simple, no dependencies | State is scattered | Small apps |
| useReducer | Centralized logic | Learning curve | Medium apps |
| Context | Cross-component sharing | Easy to overuse | Global config |
| Redux | Powerful toolchain | Lots of boilerplate | Large apps |
| Zustand | Lightweight and simple | Smaller community | Small to medium apps |
4. State management patterns
4.1 Atomic state (Jotai)
import { atom } from 'jotai';
import { useAtom } from 'jotai';
const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2);
function Counter() {
const [count, setCount] = useAtom(countAtom);
const [doubled] = useAtom(doubledAtom);
return (
<div>
<p>计数: {count}</p>
<p>翻倍: {doubled}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
</div>
);
}
4.2 Signals
import { signal, computed } from '@preact/signals-react';
const count = signal(0);
const doubled = computed(() => count.value * 2);
function Counter() {
return (
<div>
<p>计数: {count}</p>
<p>翻倍: {doubled}</p>
<button onClick={() => count.value++}>增加</button>
</div>
);
}
5. Summary
Suggestions for choosing a state management solution:
- Small apps: useState + useReducer is enough
- Medium apps: Context + custom Hook, or Zustand
- Large apps: Redux Toolkit or other mature solutions
Core principles: minimize state, single source of truth, immutable updates.