一、Context API 深度应用
1. 核心实现原理
通过createContext
创建上下文对象,使用Provider组件包裹需要共享状态的组件树,子组件通过useContext
Hook或Consumer组件消费数据。
代码示例(主题切换场景):
// 创建上下文(带类型定义)
type ThemeContextType = {
theme: 'light' | 'dark';
toggleTheme: () => void;
};
const ThemeContext = createContext<ThemeContextType | null>(null);
// Provider组件封装
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
// 使用useCallback避免重复渲染
const toggleTheme = useCallback(() => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
}, []);
// 使用useMemo优化对象引用
const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};
// 消费组件
const ThemeButton = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error("Missing ThemeProvider");
return (
<button
style={{
background: context.theme === 'dark' ? '#333' : '#fff',
color: context.theme === 'dark' ? '#fff' : '#333'
}}
onClick={context.toggleTheme}
>
Toggle Theme
</button>
);
};
最佳实践:
- 类型安全:结合TypeScript定义上下文类型
- 性能优化:使用
useMemo
/useCallback
避免无效渲染 - 错误边界:强制Provider包裹检查
- 模块化:按业务域拆分多个Context
二、Redux 现代工程实践
1. 架构演进
推荐使用Redux Toolkit(RTK)简化传统Redux的模板代码,结合React-Redux实现高效状态管理。
代码示例(计数器场景):
// store.ts
import { configureStore, createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1 },
decrement: state => { state.value -= 1 },
incrementBy: (state, action: PayloadAction<number>) => {
state.value += action.payload
}
}
});
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// App.tsx
import { Provider } from 'react-redux';
import { useAppSelector, useAppDispatch } from './hooks';
const CounterDisplay = () => {
const count = useAppSelector(state => state.counter.value);
return <div>{count}</div>;
};
const CounterControls = () => {
const dispatch = useAppDispatch();
return (
<>
<button onClick={() => dispatch(counterSlice.actions.increment())}>+</button>
<button onClick={() => dispatch(counterSlice.actions.decrement())}>-</button>
</>
);
};
核心优势:
- 不可变数据管理(通过Immer实现)
- 中间件支持(Redux-Thunk/Saga)
- 时间旅行调试(Redux DevTools)
- 类型安全(TypeScript深度集成)
三、选型决策树
维度 | Context API | Redux |
---|---|---|
适用场景 | 中小型应用/局部状态共享 | 大型复杂应用/全局状态管理 |
学习曲线 | 低(React内置) | 中高(需掌握中间件等概念) |
性能优化 | 需手动优化 | 内置性能优化 |
调试能力 | 基础React DevTools | 时间旅行调试 11 |
异步处理 | 需结合useEffect/自定义Hook | 内置中间件支持 |
四、工程化建议
-
状态分层策略
- 组件级:
useState
/useReducer
- 模块级:Context API
- 应用级:Redux
- 服务级:React Query/SWR
- 组件级:
-
性能优化要点
- Context:拆分高频/低频更新Context
- Redux:使用
reselect
创建记忆化selector - 通用:避免在渲染函数中创建新对象
-
代码规范
// Bad: 直接传递新对象导致无效渲染 <MyContext.Provider value={{ theme, toggleTheme }}> // Good: 使用useMemo优化 const value = useMemo(() => ({ theme, toggleTheme }), [theme])
-
错误处理
- 添加状态变更日志
- 使用Redux中间件统一错误处理
- 实现Context兜底默认值
五、常见陷阱及解决方案
-
Context渲染风暴
- 现象:Provider值变化导致所有消费者重新渲染
- 方案:拆分Context / 使用
memo
-
Redux状态冗余
- 现象:store中存储非全局状态
- 方案:遵循最小状态原则
-
异步状态竞争
// 使用AbortController取消过期请求 const fetchUser = createAsyncThunk( 'user/fetch', async (userId, { signal }) => { const response = await fetch(`/users/${userId}`, { signal }); return response.json(); } );
在工程实践中,建议:
- 中小型项目优先使用Context API + TypeScript
- 复杂应用采用Redux Toolkit + RTK Query
- 混合方案:Redux管理核心业务流,Context处理UI状态
最终选型需综合考虑项目规模、团队经验和长期维护成本。对于新项目,可以从Context API起步,随着复杂度增长逐步引入Redux。