在 React 中,直接使用 纯 localStorage 与 Context + useReducer + localStorage 的状态管理方案有显著区别,主要体现在 状态同步能力、代码架构 和 维护性 等方面。以下是详细对比:
1. 纯 localStorage 方案
实现方式
直接在组件中读写 localStorage
,无全局状态管理:
function ThemeSwitcher() {
const [theme, setTheme] = useState(
() => localStorage.getItem('theme') || 'light'
);
const handleChange = (newTheme) => {
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
};
return (
<select value={theme} onChange={(e) => handleChange(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}
特点
优点 | 缺点 |
---|---|
1. 实现简单,无需额外库 | 1. 状态不同步:多个组件无法实时共享同一份数据 |
2. 适合极简场景 | 2. 重复代码:每个组件需单独处理存储逻辑 |
3. 无性能开销(仅读写存储) | 3. 难以维护:业务复杂时逻辑分散 |
2. Context + useReducer + localStorage 方案
实现方式
将状态提升到全局,通过 Context 共享,并自动同步到 localStorage
:
// 1. 定义状态和 reducer
const initialState = { theme: 'light' };
function reducer(state, action) {
switch (action.type) {
case 'SET_THEME':
return { ...state, theme: action.payload };
default:
return state;
}
}
// 2. 创建 Context 和 Provider
const AppContext = createContext();
function AppProvider({ children }) {
const [state, dispatch] = useReducer(
reducer,
JSON.parse(localStorage.getItem('appState')) || initialState
);
// 自动同步到 localStorage
useEffect(() => {
localStorage.setItem('appState', JSON.stringify(state));
}, [state]);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
}
// 3. 在组件中使用
function ThemeSwitcher() {
const { state, dispatch } = useContext(AppContext);
return (
<select
value={state.theme}
onChange={(e) => dispatch({ type: 'SET_THEME', payload: e.target.value })}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}
特点
优点 | 缺点 |
---|---|
1. 状态全局共享:所有组件实时响应变化 | 1. 代码量稍多(需设置 Context/Reducer) |
2. 逻辑集中:易于维护和扩展 | 2. 小型项目可能过度设计 |
3. 自动持久化:状态变更自动同步到存储 | 3. 需处理 Provider 嵌套问题 |
核心区别对比
对比维度 | 纯 localStorage | Context + useReducer + localStorage |
---|---|---|
状态同步 | 需手动触发,组件间不同步 | 自动同步,全局状态一致 |
代码组织 | 逻辑分散在各组件 | 集中管理,高内聚低耦合 |
维护性 | 难扩展,易出现重复代码 | 易于扩展和维护 |
性能 | 直接操作存储,无额外开销 | 有 Context 的渲染开销(可通过 memo 优化) |
适用场景 | 简单页面、独立组件 | 中大型应用、需共享状态的场景 |
如何选择?
纯 localStorage
- 适合:
- 简单页面(如个人博客的主题切换)
- 独立组件(如一个无需共享的表单草稿)
- 示例:
// 独立计数器,无需共享状态 function Counter() { const [count, setCount] = useState(Number(localStorage.getItem('count')) || 0); useEffect(() => { localStorage.setItem('count', String(count)); }, [count]); return <button onClick={() => setCount(c => c + 1)}>{count}</button>; }
- 适合:
Context + useReducer + localStorage
- 适合:
- 多组件共享状态(如用户登录信息、全局主题)
- 复杂交互(如购物车、表单多步骤流程)
- 示例:
// 全局购物车状态 function Cart() { const { state, dispatch } = useContext(AppContext); return ( <div> {state.cartItems.map(item => <CartItem key={item.id} item={item} />)} </div> ); }
- 适合:
更优解:结合 Zustand
如果觉得 Context + useReducer 太重,但需要全局状态管理,推荐使用 Zustand(轻量级状态库,内置持久化):
import create from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create(persist(
(set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
}),
{
name: 'app-storage', // localStorage 的 key
}
));
// 在组件中使用
function ThemeSwitcher() {
const theme = useStore(state => state.theme);
const setTheme = useStore(state => state.setTheme);
return (
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
);
}
总结
- 直接 localStorage:简单粗暴,适合局部状态。
- Context + useReducer + localStorage:专业方案,适合全局状态。
- Zustand 等库:折中选择,简化全局状态管理。