学习目标
- 深入理解Context API和高级用法
- 掌握Redux Toolkit完整生态
- 学会多种状态管理方案
- 理解状态管理最佳实践
- 构建企业级状态管理应用
学习时间安排
总时长:8-9小时
- Context API深入:2.5小时
- Redux Toolkit完整:3小时
- 其他状态管理方案:1.5小时
- 实践项目:2-3小时
第一部分:Context API深入 (2.5小时)
1.1 高级Context模式
多层Context嵌套(详细注释版)
javascript
// src/context/ThemeContext.js
// 导入React和createContext
import React, { createContext, useContext, useState, useEffect } from 'react';
// 创建ThemeContext
export const ThemeContext = createContext();
// 定义主题配置
const themes = {
light: {
name: 'Light',
colors: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
info: '#17a2b8',
background: '#ffffff',
surface: '#f8f9fa',
text: '#212529',
textSecondary: '#6c757d'
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px'
},
typography: {
fontFamily: 'Arial, sans-serif',
fontSize: {
xs: '12px',
sm: '14px',
md: '16px',
lg: '18px',
xl: '20px'
}
}
},
dark: {
name: 'Dark',
colors: {
primary: '#0d6efd',
secondary: '#6c757d',
success: '#198754',
danger: '#dc3545',
warning: '#ffc107',
info: '#0dcaf0',
background: '#212529',
surface: '#343a40',
text: '#ffffff',
textSecondary: '#adb5bd'
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px'
},
typography: {
fontFamily: 'Arial, sans-serif',
fontSize: {
xs: '12px',
sm: '14px',
md: '16px',
lg: '18px',
xl: '20px'
}
}
}
};
// 定义ThemeProvider组件
export function ThemeProvider({ children }) {
// 使用useState Hook管理当前主题
const [currentTheme, setCurrentTheme] = useState('light');
// 使用useState Hook管理主题历史
const [themeHistory, setThemeHistory] = useState(['light']);
// 从localStorage恢复主题设置
useEffect(() => {
const savedTheme = localStorage.getItem('theme');
if (savedTheme && themes[savedTheme]) {
setCurrentTheme(savedTheme);
}
}, []);
// 保存主题设置到localStorage
useEffect(() => {
localStorage.setItem('theme', currentTheme);
}, [currentTheme]);
// 切换主题
const toggleTheme = () => {
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
setCurrentTheme(newTheme);
setThemeHistory(prev => [...prev, newTheme]);
};
// 设置特定主题
const setTheme = (themeName) => {
if (themes[themeName]) {
setCurrentTheme(themeName);
setThemeHistory(prev => [...prev, themeName]);
}
};
// 获取主题配置
const getTheme = () => themes[currentTheme];
// 获取主题变量
const getThemeVariable = (path) => {
const keys = path.split('.');
let value = themes[currentTheme];
for (const key of keys) {
value = value?.[key];
}
return value;
};
// 生成CSS变量
const generateCSSVariables = () => {
const theme = getTheme();
const variables = {};
// 颜色变量
Object.entries(theme.colors).forEach(([key, value]) => {
variables[`--color-${key}`] = value;
});
// 间距变量
Object.entries(theme.spacing).forEach(([key, value]) => {
variables[`--spacing-${key}`] = value;
});
// 字体变量
variables['--font-family'] = theme.typography.fontFamily;
Object.entries(theme.typography.fontSize).forEach(([key, value]) => {
variables[`--font-size-${key}`] = value;
});
return variables;
};
// 应用CSS变量到document
useEffect(() => {
const variables = generateCSSVariables();
const root = document.documentElement;
Object.entries(variables).forEach(([key, value]) => {
root.style.setProperty(key, value);
});
}, [currentTheme]);
// 定义上下文值
const contextValue = {
currentTheme,
theme: getTheme(),
themeHistory,
toggleTheme,
setTheme,
getThemeVariable,
availableThemes: Object.keys(themes)
};
// 返回ThemeContext.Provider
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
}
// 定义useTheme Hook
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
用户认证Context(详细注释版)
javascript
// src/context/AuthContext.js
// 导入React和createContext
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
// 创建AuthContext
export const AuthContext = createContext();
// 定义用户角色
export const ROLES = {
ADMIN: 'admin',
MODERATOR: 'moderator',
USER: 'user',
GUEST: 'guest'
};
// 定义权限
export const PERMISSIONS = {
READ_POSTS: 'read:posts',
WRITE_POSTS: 'write:posts',
DELETE_POSTS: 'delete:posts',
MANAGE_USERS: 'manage:users',
MANAGE_SETTINGS: 'manage:settings'
};
// 定义角色权限映射
const ROLE_PERMISSIONS = {
[ROLES.ADMIN]: [
PERMISSIONS.READ_POSTS,
PERMISSIONS.WRITE_POSTS,
PERMISSIONS.DELETE_POSTS,
PERMISSIONS.MANAGE_USERS,
PERMISSIONS.MANAGE_SETTINGS
],
[ROLES.MODERATOR]: [
PERMISSIONS.READ_POSTS,
PERMISSIONS.WRITE_POSTS,
PERMISSIONS.DELETE_POSTS
],
[ROLES.USER]: [
PERMISSIONS.READ_POSTS,
PERMISSIONS.WRITE_POSTS
],
[ROLES.GUEST]: [
PERMISSIONS.READ_POSTS
]
};
// 定义AuthProvider组件
export function AuthProvider({ children }) {
// 使用useState Hook管理认证状态
const [user, setUser] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [sessionTimeout, setSessionTimeout] = useState(null);
// 检查用户权限
const hasPermission = useCallback((permission) => {
if (!user) return false;
const userPermissions = ROLE_PERMISSIONS[user.role] || [];
return userPermissions.includes(permission);
}, [user]);
// 检查用户角色
const hasRole = useCallback((role) => {
if (!user) return false;
return user.role === role;
}, [user]);
// 检查是否为管理员
const isAdmin = useCallback(() => {
return hasRole(ROLES.ADMIN);
}, [hasRole]);
// 检查是否为版主
const isModerator = useCallback(() => {
return hasRole(ROLES.MODERATOR) || isAdmin();
}, [hasRole, isAdmin]);
// 登录函数
const login = async (credentials) => {
try {
setIsLoading(true);
setError(null);
// 模拟API调用
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
});
if (!response.ok) {
throw new Error('Login failed');
}
const userData = await response.json();
// 设置用户信息
setUser(userData);
setIsAuthenticated(true);
// 设置会话超时
const timeout = setTimeout(() => {
logout();
}, 30 * 60 * 1000); // 30分钟
setSessionTimeout(timeout);
// 保存到localStorage
localStorage.setItem('user', JSON.stringify(userData));
localStorage.setItem('isAuthenticated', 'true');
} catch (err) {
setError(err.message);
throw err;
} finally {
setIsLoading(false);
}
};
// 登出函数
const logout = useCallback(() => {
setUser(null);
setIsAuthenticated(false);
setError(null);
// 清除会话超时
if (sessionTimeout) {
clearTimeout(sessionTimeout);
setSessionTimeout(null);
}
// 清除localStorage
localStorage.removeItem('user');
localStorage.removeItem('isAuthenticated');
}, [sessionTimeout]);
// 更新用户信息
const updateUser = useCallback((updates) => {
if (user) {
const updatedUser = { ...user, ...updates };
setUser(updatedUser);
localStorage.setItem('user', JSON.stringify(updatedUser));
}
}, [user]);
// 刷新令牌
const refreshToken = async () => {
try {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${user?.token}`
}
});
if (response.ok) {
const { token } = await response.json();
updateUser({ token });
} else {
logout();
}
} catch (err) {
logout();
}
};
// 从localStorage恢复认证状态
useEffect(() => {
const savedUser = localStorage.getItem('user');
const savedAuth = localStorage.getItem('isAuthenticated');
if (savedUser && savedAuth === 'true') {
try {
const userData = JSON.parse(savedUser);
setUser(userData);
setIsAuthenticated(true);
} catch (err) {
console.error('Error parsing saved user data:', err);
localStorage.removeItem('user');
localStorage.removeItem('isAuthenticated');
}
}
setIsLoading(false);
}, []);
// 定期刷新令牌
useEffect(() => {
if (isAuthenticated && user?.token) {
const interval = setInterval(refreshToken, 15 * 60 * 1000); // 15分钟
return () => clearInterval(interval);
}
}, [isAuthenticated, user?.token]);
// 定义上下文值
const contextValue = {
user,
isAuthenticated,
isLoading,
error,
login,
logout,
updateUser,
hasPermission,
hasRole,
isAdmin,
isModerator,
roles: ROLES,
permissions: PERMISSIONS
};
// 返回AuthContext.Provider
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
}
// 定义useAuth Hook
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
1.2 高级Context组合
多层Context组合(详细注释版)
javascript
// src/context/AppContext.js
// 导入React和createContext
import React, { createContext, useContext, useReducer, useMemo } from 'react';
// 导入其他Context
import { ThemeProvider, useTheme } from './ThemeContext';
import { AuthProvider, useAuth } from './AuthContext';
// 创建AppContext
export const AppContext = createContext();
// 定义应用状态
const initialState = {
notifications: [],
settings: {
language: 'en',
timezone: 'UTC',
dateFormat: 'MM/DD/YYYY',
currency: 'USD'
},
ui: {
sidebarCollapsed: false,
modalOpen: false,
loading: false
},
data: {
posts: [],
users: [],
categories: []
}
};
// 定义reducer函数
function appReducer(state, action) {
switch (action.type) {
case 'ADD_NOTIFICATION':
return {
...state,
notifications: [...state.notifications, {
id: Date.now(),
...action.payload,
timestamp: new Date().toISOString()
}]
};
case 'REMOVE_NOTIFICATION':
return {
...state,
notifications: state.notifications.filter(n => n.id !== action.payload)
};
case 'UPDATE_SETTINGS':
return {
...state,
settings: { ...state.settings, ...action.payload }
};
case 'TOGGLE_SIDEBAR':
return {
...state,
ui: { ...state.ui, sidebarCollapsed: !state.ui.sidebarCollapsed }
};
case 'SET_MODAL_OPEN':
return {
...state,
ui: { ...state.ui, modalOpen: action.payload }
};
case 'SET_LOADING':
return {
...state,
ui: { ...state.ui, loading: action.payload }
};
case 'SET_POSTS':
return {
...state,
data: { ...state.data, posts: action.payload }
};
case 'SET_USERS':
return {
...state,
data: { ...state.data, users: action.payload }
};
case 'SET_CATEGORIES':
return {
...state,
data: { ...state.data, categories: action.payload }
};
default:
return state;
}
}
// 定义AppProvider组件
export function AppProvider({ children }) {
// 使用useReducer Hook管理状态
const [state, dispatch] = useReducer(appReducer, initialState);
// 定义动作函数
const actions = useMemo(() => ({
addNotification: (notification) =>
dispatch({ type: 'ADD_NOTIFICATION', payload: notification }),
removeNotification: (id) =>
dispatch({ type: 'REMOVE_NOTIFICATION', payload: id }),
updateSettings: (settings) =>
dispatch({ type: 'UPDATE_SETTINGS', payload: settings }),
toggleSidebar: () =>
dispatch({ type: 'TOGGLE_SIDEBAR' }),
setModalOpen: (open) =>
dispatch({ type: 'SET_MODAL_OPEN', payload: open }),
setLoading: (loading) =>
dispatch({ type: 'SET_LOADING', payload: loading }),
setPosts: (posts) =>
dispatch({ type: 'SET_POSTS', payload: posts }),
setUsers: (users) =>
dispatch({ type: 'SET_USERS', payload: users }),
setCategories: (categories) =>
dispatch({ type: 'SET_CATEGORIES', payload: categories })
}), []);
// 定义上下文值
const contextValue = useMemo(() => ({
state,
actions
}), [state, actions]);
// 返回AppContext.Provider
return (
<AppContext.Provider value={contextValue}>
{children}
</AppContext.Provider>
);
}
// 定义useApp Hook
export function useApp() {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within an AppProvider');
}
return context;
}
// 定义组合Provider组件
export function CombinedProvider({ children }) {
return (
<ThemeProvider>
<AuthProvider>
<AppProvider>
{children}
</AppProvider>
</AuthProvider>
</ThemeProvider>
);
}
1.3 Context性能优化
性能优化Context(详细注释版)
javascript
// src/context/PerformanceContext.js
// 导入React和createContext
import React, { createContext, useContext, useState, useMemo, useCallback } from 'react';
// 创建PerformanceContext
export const PerformanceContext = createContext();
// 定义PerformanceProvider组件
export function PerformanceProvider({ children }) {
// 使用useState Hook管理性能状态
const [metrics, setMetrics] = useState({
renderCount: 0,
lastRenderTime: 0,
averageRenderTime: 0,
memoryUsage: 0,
componentCount: 0
});
// 使用useState Hook管理性能设置
const [settings, setSettings] = useState({
enableProfiling: false,
enableMemoryTracking: false,
enableRenderTracking: false,
logLevel: 'info'
});
// 记录渲染时间
const recordRenderTime = useCallback((componentName, renderTime) => {
if (settings.enableRenderTracking) {
setMetrics(prev => ({
...prev,
renderCount: prev.renderCount + 1,
lastRenderTime: renderTime,
averageRenderTime: (prev.averageRenderTime + renderTime) / 2
}));
if (settings.logLevel === 'debug') {
console.log(`Component ${componentName} rendered in ${renderTime}ms`);
}
}
}, [settings.enableRenderTracking, settings.logLevel]);
// 记录内存使用
const recordMemoryUsage = useCallback(() => {
if (settings.enableMemoryTracking && performance.memory) {
const memory = performance.memory;
setMetrics(prev => ({
...prev,
memoryUsage: memory.usedJSHeapSize
}));
}
}, [settings.enableMemoryTracking]);
// 记录组件数量
const recordComponentCount = useCallback((count) => {
setMetrics(prev => ({
...prev,
componentCount: count
}));
}, []);
// 重置性能指标
const resetMetrics = useCallback(() => {
setMetrics({
renderCount: 0,
lastRenderTime: 0,
averageRenderTime: 0,
memoryUsage: 0,
componentCount: 0
});
}, []);
// 更新设置
const updateSettings = useCallback((newSettings) => {
setSettings(prev => ({ ...prev, ...newSettings }));
}, []);
// 获取性能报告
const getPerformanceReport = useCallback(() => {
return {
...metrics,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
};
}, [metrics]);
// 导出性能数据
const exportPerformanceData = useCallback(() => {
const report = getPerformanceReport();
const dataStr = JSON.stringify(report, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `performance-report-${Date.now()}.json`;
link.click();
URL.revokeObjectURL(url);
}, [getPerformanceReport]);
// 定义上下文值
const contextValue = useMemo(() => ({
metrics,
settings,
recordRenderTime,
recordMemoryUsage,
recordComponentCount,
resetMetrics,
updateSettings,
getPerformanceReport,
exportPerformanceData
}), [
metrics,
settings,
recordRenderTime,
recordMemoryUsage,
recordComponentCount,
resetMetrics,
updateSettings,
getPerformanceReport,
exportPerformanceData
]);
// 返回PerformanceContext.Provider
return (
<PerformanceContext.Provider value={contextValue}>
{children}
</PerformanceContext.Provider>
);
}
// 定义usePerformance Hook
export function usePerformance() {
const context = useContext(PerformanceContext);
if (!context) {
throw new Error('usePerformance must be used within a PerformanceProvider');
}
return context;
}
第二部分:Redux Toolkit完整生态 (3小时)
2.1 高级Redux配置
完整Store配置(详细注释版)
javascript
// src/store/store.js
// 导入configureStore
import { configureStore } from '@reduxjs/toolkit';
// 导入reducers
import counterReducer from './slices/counterSlice';
import userReducer from './slices/userSlice';
import todoReducer from './slices/todoSlice';
import authReducer from './slices/authSlice';
import uiReducer from './slices/uiSlice';
// 导入中间件
import { authMiddleware } from './middleware/authMiddleware';
import { loggerMiddleware } from './middleware/loggerMiddleware';
import { errorMiddleware } from './middleware/errorMiddleware';
// 配置Redux store
export const store = configureStore({
// reducer配置
reducer: {
counter: counterReducer,
user: userReducer,
todos: todoReducer,
auth: authReducer,
ui: uiReducer
},
// 中间件配置
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
// 启用序列化检查
serializableCheck: {
ignoredActions: [
'persist/PERSIST',
'persist/REHYDRATE',
'auth/login/fulfilled',
'auth/register/fulfilled'
],
ignoredActionsPaths: ['meta.arg', 'payload.timestamp'],
ignoredPaths: ['auth.user.lastLogin']
},
// 启用不可变性检查
immutableCheck: {
ignoredPaths: ['auth.user.lastLogin']
},
// 启用动作检查
actionCreatorCheck: {
ignoredActions: ['persist/PERSIST']
}
})
.concat(authMiddleware)
.concat(loggerMiddleware)
.concat(errorMiddleware),
// 开发工具配置
devTools: {
name: 'Task Management App',
maxAge: 50,
trace: true,
traceLimit: 25
},
// 预加载状态
preloadedState: {
auth: {
user: null,
isAuthenticated: false,
isLoading: false,
error: null
},
ui: {
theme: 'light',
language: 'en',
sidebarOpen: false,
notifications: []
}
}
});
// 导出类型
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// 导出store
export default store;
2.2 高级Slice模式
异步Thunk模式(详细注释版)
javascript
// src/store/slices/authSlice.js
// 导入createSlice和createAsyncThunk
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// 导入API服务
import { authAPI } from '../../services/authAPI';
// 定义异步thunk - 登录
export const loginUser = createAsyncThunk(
'auth/loginUser',
async (credentials, { rejectWithValue, fulfillWithValue }) => {
try {
// 调用API
const response = await authAPI.login(credentials);
// 保存令牌到localStorage
localStorage.setItem('token', response.token);
localStorage.setItem('refreshToken', response.refreshToken);
// 返回用户数据
return fulfillWithValue(response);
} catch (error) {
// 返回错误信息
return rejectWithValue(error.message);
}
}
);
// 定义异步thunk - 注册
export const registerUser = createAsyncThunk(
'auth/registerUser',
async (userData, { rejectWithValue }) => {
try {
const response = await authAPI.register(userData);
return response;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// 定义异步thunk - 刷新令牌
export const refreshToken = createAsyncThunk(
'auth/refreshToken',
async (_, { getState, rejectWithValue }) => {
try {
const state = getState();
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
throw new Error('No refresh token available');
}
const response = await authAPI.refreshToken(refreshToken);
// 更新令牌
localStorage.setItem('token', response.token);
localStorage.setItem('refreshToken', response.refreshToken);
return response;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// 定义异步thunk - 获取用户信息
export const fetchUserProfile = createAsyncThunk(
'auth/fetchUserProfile',
async (_, { getState, rejectWithValue }) => {
try {
const state = getState();
const token = localStorage.getItem('token');
if (!token) {
throw new Error('No token available');
}
const response = await authAPI.getProfile(token);
return response;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// 定义初始状态
const initialState = {
user: null,
isAuthenticated: false,
isLoading: false,
error: null,
token: null,
refreshToken: null,
lastActivity: null,
sessionTimeout: null
};
// 创建authSlice
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
// 清除错误
clearError: (state) => {
state.error = null;
},
// 设置加载状态
setLoading: (state, action) => {
state.isLoading = action.payload;
},
// 更新用户信息
updateUser: (state, action) => {
if (state.user) {
state.user = { ...state.user, ...action.payload };
}
},
// 更新最后活动时间
updateLastActivity: (state) => {
state.lastActivity = new Date().toISOString();
},
// 设置会话超时
setSessionTimeout: (state, action) => {
state.sessionTimeout = action.payload;
},
// 清除会话超时
clearSessionTimeout: (state) => {
state.sessionTimeout = null;
},
// 登出
logout: (state) => {
state.user = null;
state.isAuthenticated = false;
state.token = null;
state.refreshToken = null;
state.lastActivity = null;
state.sessionTimeout = null;
state.error = null;
// 清除localStorage
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
}
},
// 处理异步thunk的额外reducers
extraReducers: (builder) => {
builder
// 处理登录
.addCase(loginUser.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(loginUser.fulfilled, (state, action) => {
state.isLoading = false;
state.user = action.payload.user;
state.isAuthenticated = true;
state.token = action.payload.token;
state.refreshToken = action.payload.refreshToken;
state.lastActivity = new Date().toISOString();
state.error = null;
})
.addCase(loginUser.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload;
})
// 处理注册
.addCase(registerUser.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(registerUser.fulfilled, (state, action) => {
state.isLoading = false;
state.user = action.payload.user;
state.isAuthenticated = true;
state.token = action.payload.token;
state.refreshToken = action.payload.refreshToken;
state.error = null;
})
.addCase(registerUser.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload;
})
// 处理刷新令牌
.addCase(refreshToken.fulfilled, (state, action) => {
state.token = action.payload.token;
state.refreshToken = action.payload.refreshToken;
state.lastActivity = new Date().toISOString();
})
.addCase(refreshToken.rejected, (state) => {
state.user = null;
state.isAuthenticated = false;
state.token = null;
state.refreshToken = null;
})
// 处理获取用户信息
.addCase(fetchUserProfile.fulfilled, (state, action) => {
state.user = action.payload;
state.isAuthenticated = true;
})
.addCase(fetchUserProfile.rejected, (state) => {
state.user = null;
state.isAuthenticated = false;
});
}
});
// 导出actions
export const {
clearError,
setLoading,
updateUser,
updateLastActivity,
setSessionTimeout,
clearSessionTimeout,
logout
} = authSlice.actions;
// 导出reducer
export default authSlice.reducer;
2.3 自定义中间件
认证中间件(详细注释版)
javascript
// src/store/middleware/authMiddleware.js
// 导入createListenerMiddleware
import { createListenerMiddleware } from '@reduxjs/toolkit';
// 导入actions
import { logout, updateLastActivity } from '../slices/authSlice';
// 创建监听中间件
export const authListenerMiddleware = createListenerMiddleware();
// 监听用户活动
authListenerMiddleware.startListening({
actionCreator: updateLastActivity,
effect: (action, listenerApi) => {
// 更新最后活动时间
const now = new Date();
console.log('User activity updated:', now.toISOString());
}
});
// 监听登出
authListenerMiddleware.startListening({
actionCreator: logout,
effect: (action, listenerApi) => {
// 清除所有定时器
const state = listenerApi.getState();
if (state.auth.sessionTimeout) {
clearTimeout(state.auth.sessionTimeout);
}
// 清除localStorage
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
console.log('User logged out');
}
});
// 导出中间件
export const authMiddleware = authListenerMiddleware.middleware;
日志中间件(详细注释版)
javascript
// src/store/middleware/loggerMiddleware.js
// 创建日志中间件
export const loggerMiddleware = (store) => (next) => (action) => {
// 记录动作开始时间
const startTime = performance.now();
// 记录动作信息
console.group(`Action: ${action.type}`);
console.log('Previous state:', store.getState());
console.log('Action:', action);
// 执行动作
const result = next(action);
// 记录动作结束时间
const endTime = performance.now();
const duration = endTime - startTime;
// 记录新状态
console.log('Next state:', store.getState());
console.log(`Duration: ${duration.toFixed(2)}ms`);
console.groupEnd();
// 如果动作执行时间过长,发出警告
if (duration > 100) {
console.warn(`Slow action detected: ${action.type} took ${duration.toFixed(2)}ms`);
}
return result;
};
错误中间件(详细注释版)
javascript
// src/store/middleware/errorMiddleware.js
// 创建错误中间件
export const errorMiddleware = (store) => (next) => (action) => {
try {
// 执行动作
const result = next(action);
// 检查是否有错误
if (action.type.endsWith('/rejected')) {
const error = action.payload || action.error;
// 记录错误
console.error('Action rejected:', action.type, error);
// 发送错误到错误报告服务
if (process.env.NODE_ENV === 'production') {
// 这里可以集成错误报告服务,如Sentry
// Sentry.captureException(error);
}
}
return result;
} catch (error) {
// 捕获中间件中的错误
console.error('Middleware error:', error);
// 发送错误到错误报告服务
if (process.env.NODE_ENV === 'production') {
// Sentry.captureException(error);
}
throw error;
}
};
第三部分:其他状态管理方案 (1.5小时)
3.1 Zustand状态管理
安装Zustand
bash
npm install zustand
Zustand Store(详细注释版)
javascript
// src/stores/useTaskStore.js
// 导入create
import { create } from 'zustand';
// 导入devtools
import { devtools } from 'zustand/middleware';
// 定义任务类型
const TaskTypes = {
TODO: 'todo',
IN_PROGRESS: 'in_progress',
DONE: 'done'
};
// 定义优先级
const Priorities = {
LOW: 'low',
MEDIUM: 'medium',
HIGH: 'high',
URGENT: 'urgent'
};
// 创建任务store
export const useTaskStore = create(
devtools(
(set, get) => ({
// 状态
tasks: [],
filter: 'all',
sortBy: 'createdAt',
isLoading: false,
error: null,
// 动作
addTask: (task) => {
const newTask = {
id: Date.now(),
...task,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
set((state) => ({
tasks: [...state.tasks, newTask]
}));
},
updateTask: (id, updates) => {
set((state) => ({
tasks: state.tasks.map(task =>
task.id === id
? { ...task, ...updates, updatedAt: new Date().toISOString() }
: task
)
}));
},
deleteTask: (id) => {
set((state) => ({
tasks: state.tasks.filter(task => task.id !== id)
}));
},
toggleTask: (id) => {
set((state) => ({
tasks: state.tasks.map(task =>
task.id === id
? { ...task, completed: !task.completed, updatedAt: new Date().toISOString() }
: task
)
}));
},
setFilter: (filter) => {
set({ filter });
},
setSortBy: (sortBy) => {
set({ sortBy });
},
setLoading: (loading) => {
set({ isLoading: loading });
},
setError: (error) => {
set({ error });
},
clearError: () => {
set({ error: null });
},
// 计算属性
getFilteredTasks: () => {
const { tasks, filter } = get();
switch (filter) {
case 'completed':
return tasks.filter(task => task.completed);
case 'pending':
return tasks.filter(task => !task.completed);
case 'high':
return tasks.filter(task => task.priority === 'high');
case 'medium':
return tasks.filter(task => task.priority === 'medium');
case 'low':
return tasks.filter(task => task.priority === 'low');
default:
return tasks;
}
},
getSortedTasks: () => {
const { getFilteredTasks, sortBy } = get();
const filteredTasks = getFilteredTasks();
return [...filteredTasks].sort((a, b) => {
switch (sortBy) {
case 'title':
return a.title.localeCompare(b.title);
case 'priority':
const priorityOrder = { urgent: 4, high: 3, medium: 2, low: 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
case 'createdAt':
return new Date(b.createdAt) - new Date(a.createdAt);
case 'updatedAt':
return new Date(b.updatedAt) - new Date(a.updatedAt);
default:
return 0;
}
});
},
getTaskStats: () => {
const { tasks } = get();
return {
total: tasks.length,
completed: tasks.filter(task => task.completed).length,
pending: tasks.filter(task => !task.completed).length,
highPriority: tasks.filter(task => task.priority === 'high').length,
mediumPriority: tasks.filter(task => task.priority === 'medium').length,
lowPriority: tasks.filter(task => task.priority === 'low').length,
completionRate: tasks.length > 0
? Math.round((tasks.filter(task => task.completed).length / tasks.length) * 100)
: 0
};
}
}),
{
name: 'task-store',
partialize: (state) => ({
tasks: state.tasks,
filter: state.filter,
sortBy: state.sortBy
})
}
)
);
// 导出类型和常量
export { TaskTypes, Priorities };
3.2 Jotai原子状态管理
安装Jotai
bash
npm install jotai
Jotai原子(详细注释版)
javascript
// src/atoms/taskAtoms.js
// 导入atom
import { atom } from 'jotai';
// 导入工具函数
import { atomWithStorage } from 'jotai/utils';
// 定义任务原子
export const tasksAtom = atomWithStorage('tasks', []);
// 定义过滤器原子
export const filterAtom = atom('all');
// 定义排序原子
export const sortByAtom = atom('createdAt');
// 定义加载状态原子
export const isLoadingAtom = atom(false);
// 定义错误原子
export const errorAtom = atom(null);
// 定义编辑任务原子
export const editingTaskAtom = atom(null);
// 定义过滤后的任务原子
export const filteredTasksAtom = atom((get) => {
const tasks = get(tasksAtom);
const filter = get(filterAtom);
switch (filter) {
case 'completed':
return tasks.filter(task => task.completed);
case 'pending':
return tasks.filter(task => !task.completed);
case 'high':
return tasks.filter(task => task.priority === 'high');
case 'medium':
return tasks.filter(task => task.priority === 'medium');
case 'low':
return tasks.filter(task => task.priority === 'low');
default:
return tasks;
}
});
// 定义排序后的任务原子
export const sortedTasksAtom = atom((get) => {
const tasks = get(filteredTasksAtom);
const sortBy = get(sortByAtom);
return [...tasks].sort((a, b) => {
switch (sortBy) {
case 'title':
return a.title.localeCompare(b.title);
case 'priority':
const priorityOrder = { urgent: 4, high: 3, medium: 2, low: 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
case 'createdAt':
return new Date(b.createdAt) - new Date(a.createdAt);
case 'updatedAt':
return new Date(b.updatedAt) - new Date(a.updatedAt);
default:
return 0;
}
});
});
// 定义任务统计原子
export const taskStatsAtom = atom((get) => {
const tasks = get(tasksAtom);
return {
total: tasks.length,
completed: tasks.filter(task => task.completed).length,
pending: tasks.filter(task => !task.completed).length,
highPriority: tasks.filter(task => task.priority === 'high').length,
mediumPriority: tasks.filter(task => task.priority === 'medium').length,
lowPriority: tasks.filter(task => task.priority === 'low').length,
completionRate: tasks.length > 0
? Math.round((tasks.filter(task => task.completed).length / tasks.length) * 100)
: 0
};
});
// 定义动作原子
export const addTaskAtom = atom(
null,
(get, set, newTask) => {
const tasks = get(tasksAtom);
const task = {
id: Date.now(),
...newTask,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
set(tasksAtom, [...tasks, task]);
}
);
export const updateTaskAtom = atom(
null,
(get, set, { id, updates }) => {
const tasks = get(tasksAtom);
const updatedTasks = tasks.map(task =>
task.id === id
? { ...task, ...updates, updatedAt: new Date().toISOString() }
: task
);
set(tasksAtom, updatedTasks);
}
);
export const deleteTaskAtom = atom(
null,
(get, set, id) => {
const tasks = get(tasksAtom);
const filteredTasks = tasks.filter(task => task.id !== id);
set(tasksAtom, filteredTasks);
}
);
export const toggleTaskAtom = atom(
null,
(get, set, id) => {
const tasks = get(tasksAtom);
const updatedTasks = tasks.map(task =>
task.id === id
? { ...task, completed: !task.completed, updatedAt: new Date().toISOString() }
: task
);
set(tasksAtom, updatedTasks);
}
);
第四部分:实践项目(详细注释版)
项目:企业级任务管理系统
主应用组件(详细注释版)
javascript
// src/App.js
// 导入React
import React from 'react';
// 导入Provider
import { Provider } from 'react-redux';
// 导入store
import { store } from './store/store';
// 导入Context Providers
import { CombinedProvider } from './context/AppContext';
// 导入组件
import TaskManager from './components/TaskManager';
import Header from './components/Header';
import Sidebar from './components/Sidebar';
import NotificationCenter from './components/NotificationCenter';
// 导入样式
import './App.css';
// 定义主应用组件
function App() {
return (
<Provider store={store}>
<CombinedProvider>
<div className="App">
<Header />
<div className="App-body">
<Sidebar />
<main className="App-main">
<TaskManager />
</main>
</div>
<NotificationCenter />
</div>
</CombinedProvider>
</Provider>
);
}
// 导出App组件
export default App;
任务管理器组件(详细注释版)
javascript
// src/components/TaskManager.js
// 导入React和useState
import React, { useState, useEffect } from 'react';
// 导入useSelector和useDispatch
import { useSelector, useDispatch } from 'react-redux';
// 导入actions
import {
addTodo,
updateTodo,
deleteTodo,
toggleTodo,
setFilter,
setSortBy,
clearAllTodos,
clearCompletedTodos
} from '../store/slices/todoSlice';
// 导入子组件
import TaskForm from './TaskForm';
import TaskList from './TaskList';
import TaskStats from './TaskStats';
import TaskFilters from './TaskFilters';
import TaskBulkActions from './TaskBulkActions';
// 导入自定义Hooks
import { useTaskStore } from '../stores/useTaskStore';
import { useApp } from '../context/AppContext';
import { useTheme } from '../context/ThemeContext';
import { useAuth } from '../context/AuthContext';
// 定义TaskManager组件
function TaskManager() {
// 使用Redux状态
const { items, filter, sortBy, isLoading, error } = useSelector(state => state.todos);
const dispatch = useDispatch();
// 使用Zustand状态
const {
tasks: zustandTasks,
addTask: addZustandTask,
updateTask: updateZustandTask,
deleteTask: deleteZustandTask,
toggleTask: toggleZustandTask,
getTaskStats
} = useTaskStore();
// 使用Context状态
const { state: appState, actions: appActions } = useApp();
const { theme } = useTheme();
const { user, hasPermission } = useAuth();
// 使用useState Hook管理本地状态
const [showForm, setShowForm] = useState(false);
const [editingTask, setEditingTask] = useState(null);
const [selectedTasks, setSelectedTasks] = useState([]);
const [viewMode, setViewMode] = useState('grid'); // grid, list, kanban
const [searchQuery, setSearchQuery] = useState('');
// 同步Redux和Zustand状态
useEffect(() => {
if (items.length !== zustandTasks.length) {
// 同步状态
items.forEach(item => {
if (!zustandTasks.find(task => task.id === item.id)) {
addZustandTask(item);
}
});
}
}, [items, zustandTasks, addZustandTask]);
// 处理添加任务
const handleAddTask = (taskData) => {
// 使用Redux
dispatch(addTodo(taskData));
// 使用Zustand
addZustandTask(taskData);
// 使用Context
appActions.addNotification({
type: 'success',
message: 'Task added successfully!'
});
setShowForm(false);
};
// 处理更新任务
const handleUpdateTask = (id, updates) => {
// 使用Redux
dispatch(updateTodo({ id, updates }));
// 使用Zustand
updateZustandTask(id, updates);
// 使用Context
appActions.addNotification({
type: 'info',
message: 'Task updated successfully!'
});
setEditingTask(null);
setShowForm(false);
};
// 处理删除任务
const handleDeleteTask = (id) => {
// 使用Redux
dispatch(deleteTodo(id));
// 使用Zustand
deleteZustandTask(id);
// 使用Context
appActions.addNotification({
type: 'warning',
message: 'Task deleted successfully!'
});
};
// 处理切换任务状态
const handleToggleTask = (id) => {
// 使用Redux
dispatch(toggleTodo(id));
// 使用Zustand
toggleZustandTask(id);
};
// 处理批量操作
const handleBulkAction = (action) => {
switch (action) {
case 'delete':
selectedTasks.forEach(id => handleDeleteTask(id));
setSelectedTasks([]);
break;
case 'complete':
selectedTasks.forEach(id => handleToggleTask(id));
setSelectedTasks([]);
break;
case 'archive':
// 实现归档功能
break;
}
};
// 处理搜索
const handleSearch = (query) => {
setSearchQuery(query);
};
// 获取任务统计
const stats = getTaskStats();
// 返回JSX元素
return (
<div className="task-manager" style={{ backgroundColor: theme.colors.background }}>
<div className="task-header">
<h2>Task Manager</h2>
<div className="task-controls">
<input
type="text"
placeholder="Search tasks..."
value={searchQuery}
onChange={(e) => handleSearch(e.target.value)}
className="search-input"
/>
<select
value={viewMode}
onChange={(e) => setViewMode(e.target.value)}
className="view-mode-select"
>
<option value="grid">Grid View</option>
<option value="list">List View</option>
<option value="kanban">Kanban View</option>
</select>
{hasPermission('write:tasks') && (
<button
className="add-task-btn"
onClick={() => setShowForm(true)}
>
Add New Task
</button>
)}
</div>
</div>
{/* 任务统计 */}
<TaskStats stats={stats} />
{/* 任务过滤器 */}
<TaskFilters
filter={filter}
sortBy={sortBy}
onFilterChange={(newFilter) => dispatch(setFilter(newFilter))}
onSortChange={(newSort) => dispatch(setSortBy(newSort))}
/>
{/* 批量操作 */}
{selectedTasks.length > 0 && (
<TaskBulkActions
selectedCount={selectedTasks.length}
onBulkAction={handleBulkAction}
onClearSelection={() => setSelectedTasks([])}
/>
)}
{/* 任务表单 */}
{showForm && (
<TaskForm
task={editingTask}
onSubmit={editingTask ?
(data) => handleUpdateTask(editingTask.id, data) :
handleAddTask
}
onCancel={() => {
setEditingTask(null);
setShowForm(false);
}}
/>
)}
{/* 任务列表 */}
<TaskList
tasks={items}
viewMode={viewMode}
searchQuery={searchQuery}
selectedTasks={selectedTasks}
onEdit={(task) => {
setEditingTask(task);
setShowForm(true);
}}
onDelete={handleDeleteTask}
onToggle={handleToggleTask}
onSelectTask={(id) => {
setSelectedTasks(prev =>
prev.includes(id)
? prev.filter(taskId => taskId !== id)
: [...prev, id]
);
}}
onSelectAll={(selected) => {
setSelectedTasks(selected ? items.map(task => task.id) : []);
}}
/>
{/* 操作按钮 */}
<div className="task-actions">
<button
className="clear-completed-btn"
onClick={() => dispatch(clearCompletedTodos())}
disabled={items.filter(task => task.completed).length === 0}
>
Clear Completed
</button>
{hasPermission('delete:tasks') && (
<button
className="clear-all-btn"
onClick={() => {
if (window.confirm('Are you sure you want to clear all tasks?')) {
dispatch(clearAllTodos());
}
}}
disabled={items.length === 0}
>
Clear All
</button>
)}
</div>
{/* 错误显示 */}
{error && (
<div className="error-message">
Error: {error}
</div>
)}
</div>
);
}
// 导出TaskManager组件
export default TaskManager;
任务表单组件(详细注释版)
javascript
// src/components/TaskForm.js
// 导入React和useState
import React, { useState, useEffect } from 'react';
// 导入useAuth Hook
import { useAuth } from '../context/AuthContext';
// 导入useTheme Hook
import { useTheme } from '../context/ThemeContext';
// 定义TaskForm组件
function TaskForm({ task, onSubmit, onCancel }) {
// 使用Context Hooks
const { user, hasPermission } = useAuth();
const { theme } = useTheme();
// 使用useState Hook管理表单状态
const [formData, setFormData] = useState({
title: '',
description: '',
priority: 'medium',
dueDate: '',
assignee: user?.id || '',
tags: [],
estimatedHours: 0,
category: 'general'
});
// 使用useState Hook管理验证状态
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
// 当编辑任务时,更新表单数据
useEffect(() => {
if (task) {
setFormData({
title: task.title || '',
description: task.description || '',
priority: task.priority || 'medium',
dueDate: task.dueDate || '',
assignee: task.assignee || user?.id || '',
tags: task.tags || [],
estimatedHours: task.estimatedHours || 0,
category: task.category || 'general'
});
}
}, [task, user?.id]);
// 处理输入变化
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// 清除相关错误
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: null
}));
}
};
// 处理标签变化
const handleTagChange = (e) => {
const value = e.target.value;
if (e.key === 'Enter' && value.trim()) {
e.preventDefault();
setFormData(prev => ({
...prev,
tags: [...prev.tags, value.trim()]
}));
e.target.value = '';
}
};
// 移除标签
const removeTag = (index) => {
setFormData(prev => ({
...prev,
tags: prev.tags.filter((_, i) => i !== index)
}));
};
// 验证表单
const validateForm = () => {
const newErrors = {};
if (!formData.title.trim()) {
newErrors.title = 'Title is required';
}
if (formData.priority && !['low', 'medium', 'high', 'urgent'].includes(formData.priority)) {
newErrors.priority = 'Invalid priority';
}
if (formData.estimatedHours < 0) {
newErrors.estimatedHours = 'Estimated hours cannot be negative';
}
if (formData.dueDate && new Date(formData.dueDate) < new Date()) {
newErrors.dueDate = 'Due date cannot be in the past';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
// 处理表单提交
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
await onSubmit(formData);
} catch (error) {
console.error('Error submitting form:', error);
} finally {
setIsSubmitting(false);
}
};
// 返回JSX元素
return (
<div className="task-form-overlay">
<div className="task-form" style={{ backgroundColor: theme.colors.surface }}>
<h3>{task ? 'Edit Task' : 'Add New Task'}</h3>
<form onSubmit={handleSubmit}>
{/* 任务标题 */}
<div className="form-group">
<label htmlFor="title">Title *</label>
<input
type="text"
id="title"
name="title"
value={formData.title}
onChange={handleChange}
required
placeholder="Enter task title"
className={errors.title ? 'error' : ''}
/>
{errors.title && <span className="error-text">{errors.title}</span>}
</div>
{/* 任务描述 */}
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
id="description"
name="description"
value={formData.description}
onChange={handleChange}
placeholder="Enter task description"
rows="4"
/>
</div>
{/* 优先级和类别 */}
<div className="form-row">
<div className="form-group">
<label htmlFor="priority">Priority</label>
<select
id="priority"
name="priority"
value={formData.priority}
onChange={handleChange}
className={errors.priority ? 'error' : ''}
>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
</select>
{errors.priority && <span className="error-text">{errors.priority}</span>}
</div>
<div className="form-group">
<label htmlFor="category">Category</label>
<select
id="category"
name="category"
value={formData.category}
onChange={handleChange}
>
<option value="general">General</option>
<option value="development">Development</option>
<option value="design">Design</option>
<option value="testing">Testing</option>
<option value="documentation">Documentation</option>
</select>
</div>
</div>
{/* 截止日期和预估时间 */}
<div className="form-row">
<div className="form-group">
<label htmlFor="dueDate">Due Date</label>
<input
type="date"
id="dueDate"
name="dueDate"
value={formData.dueDate}
onChange={handleChange}
className={errors.dueDate ? 'error' : ''}
/>
{errors.dueDate && <span className="error-text">{errors.dueDate}</span>}
</div>
<div className="form-group">
<label htmlFor="estimatedHours">Estimated Hours</label>
<input
type="number"
id="estimatedHours"
name="estimatedHours"
value={formData.estimatedHours}
onChange={handleChange}
min="0"
step="0.5"
className={errors.estimatedHours ? 'error' : ''}
/>
{errors.estimatedHours && <span className="error-text">{errors.estimatedHours}</span>}
</div>
</div>
{/* 分配者 */}
{hasPermission('assign:tasks') && (
<div className="form-group">
<label htmlFor="assignee">Assignee</label>
<select
id="assignee"
name="assignee"
value={formData.assignee}
onChange={handleChange}
>
<option value="">Unassigned</option>
<option value={user?.id}>{user?.name} (You)</option>
{/* 这里可以添加其他用户选项 */}
</select>
</div>
)}
{/* 标签 */}
<div className="form-group">
<label htmlFor="tags">Tags</label>
<input
type="text"
id="tags"
placeholder="Press Enter to add tag"
onKeyPress={handleTagChange}
/>
<div className="tags-list">
{formData.tags.map((tag, index) => (
<span key={index} className="tag">
{tag}
<button
type="button"
onClick={() => removeTag(index)}
className="remove-tag"
>
×
</button>
</span>
))}
</div>
</div>
{/* 表单按钮 */}
<div className="form-actions">
<button
type="submit"
className="submit-btn"
disabled={isSubmitting}
>
{isSubmitting ? 'Saving...' : (task ? 'Update Task' : 'Add Task')}
</button>
<button
type="button"
onClick={onCancel}
className="cancel-btn"
>
Cancel
</button>
</div>
</form>
</div>
</div>
);
}
// 导出TaskForm组件
export default TaskForm;
样式文件(详细注释版)
css
/* src/App.css */
/* 应用主容器样式 */
.App {
min-height: 100vh;
display: flex;
flex-direction: column;
font-family: var(--font-family);
background-color: var(--color-background);
color: var(--color-text);
}
/* 应用头部样式 */
.App-header {
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
padding: var(--spacing-md);
display: flex;
justify-content: space-between;
align-items: center;
}
.App-header h1 {
margin: 0;
color: var(--color-text);
font-size: var(--font-size-lg);
}
/* 应用主体样式 */
.App-body {
display: flex;
flex: 1;
min-height: 0;
}
.App-main {
flex: 1;
padding: var(--spacing-lg);
overflow-y: auto;
}
/* 任务管理器样式 */
.task-manager {
max-width: 1200px;
margin: 0 auto;
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-lg);
flex-wrap: wrap;
gap: var(--spacing-md);
}
.task-header h2 {
margin: 0;
color: var(--color-text);
font-size: var(--font-size-xl);
}
.task-controls {
display: flex;
align-items: center;
gap: var(--spacing-md);
flex-wrap: wrap;
}
.search-input {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: var(--font-size-sm);
min-width: 200px;
}
.view-mode-select {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: var(--font-size-sm);
}
.add-task-btn {
background-color: var(--color-primary);
color: white;
border: none;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 500;
}
.add-task-btn:hover {
background-color: var(--color-primary-dark);
}
/* 任务统计样式 */
.task-stats {
background-color: var(--color-surface);
padding: var(--spacing-lg);
border-radius: 8px;
margin-bottom: var(--spacing-lg);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.task-stats h3 {
margin-top: 0;
color: var(--color-text);
font-size: var(--font-size-lg);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--spacing-md);
margin-bottom: var(--spacing-lg);
}
.stat-item {
text-align: center;
padding: var(--spacing-md);
background-color: var(--color-background);
border-radius: 8px;
border: 1px solid var(--color-border);
}
.stat-item h4 {
margin: 0 0 var(--spacing-sm) 0;
color: var(--color-text-secondary);
font-size: var(--font-size-sm);
font-weight: 500;
}
.stat-number {
font-size: var(--font-size-xl);
font-weight: bold;
color: var(--color-primary);
margin: 0;
}
/* 任务过滤器样式 */
.task-filters {
background-color: var(--color-surface);
padding: var(--spacing-md);
border-radius: 8px;
margin-bottom: var(--spacing-lg);
display: flex;
gap: var(--spacing-md);
align-items: center;
flex-wrap: wrap;
}
.task-filters select {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: var(--font-size-sm);
background-color: var(--color-background);
color: var(--color-text);
}
/* 批量操作样式 */
.task-bulk-actions {
background-color: var(--color-warning);
color: var(--color-text);
padding: var(--spacing-md);
border-radius: 8px;
margin-bottom: var(--spacing-lg);
display: flex;
justify-content: space-between;
align-items: center;
}
.bulk-actions-buttons {
display: flex;
gap: var(--spacing-sm);
}
.bulk-action-btn {
padding: var(--spacing-xs) var(--spacing-sm);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-xs);
font-weight: 500;
}
.bulk-action-btn.danger {
background-color: var(--color-danger);
color: white;
}
.bulk-action-btn.primary {
background-color: var(--color-primary);
color: white;
}
.bulk-action-btn.secondary {
background-color: var(--color-secondary);
color: white;
}
/* 任务表单样式 */
.task-form-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.task-form {
background-color: var(--color-surface);
padding: var(--spacing-xl);
border-radius: 8px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.task-form h3 {
margin-top: 0;
color: var(--color-text);
font-size: var(--font-size-lg);
}
.form-group {
margin-bottom: var(--spacing-lg);
}
.form-row {
display: flex;
gap: var(--spacing-md);
}
.form-row .form-group {
flex: 1;
}
.form-group label {
display: block;
margin-bottom: var(--spacing-xs);
font-weight: 500;
color: var(--color-text);
font-size: var(--font-size-sm);
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: var(--font-size-sm);
background-color: var(--color-background);
color: var(--color-text);
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.form-group input.error,
.form-group textarea.error,
.form-group select.error {
border-color: var(--color-danger);
}
.error-text {
color: var(--color-danger);
font-size: var(--font-size-xs);
margin-top: var(--spacing-xs);
display: block;
}
.tags-list {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-xs);
margin-top: var(--spacing-xs);
}
.tag {
background-color: var(--color-primary);
color: white;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: 4px;
font-size: var(--font-size-xs);
display: flex;
align-items: center;
gap: var(--spacing-xs);
}
.remove-tag {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: var(--font-size-sm);
padding: 0;
margin-left: var(--spacing-xs);
}
.form-actions {
display: flex;
gap: var(--spacing-md);
justify-content: center;
margin-top: var(--spacing-xl);
}
.submit-btn {
background-color: var(--color-success);
color: white;
border: none;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 500;
}
.submit-btn:hover {
background-color: var(--color-success-dark);
}
.submit-btn:disabled {
background-color: var(--color-secondary);
cursor: not-allowed;
}
.cancel-btn {
background-color: var(--color-secondary);
color: white;
border: none;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 500;
}
.cancel-btn:hover {
background-color: var(--color-secondary-dark);
}
/* 任务操作按钮样式 */
.task-actions {
display: flex;
gap: var(--spacing-md);
justify-content: center;
margin-top: var(--spacing-lg);
}
.clear-completed-btn {
background-color: var(--color-warning);
color: var(--color-text);
border: none;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 500;
}
.clear-completed-btn:hover {
background-color: var(--color-warning-dark);
}
.clear-completed-btn:disabled {
background-color: var(--color-secondary);
cursor: not-allowed;
}
.clear-all-btn {
background-color: var(--color-danger);
color: white;
border: none;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: 4px;
cursor: pointer;
font-size: var(--font-size-sm);
font-weight: 500;
}
.clear-all-btn:hover {
background-color: var(--color-danger-dark);
}
.clear-all-btn:disabled {
background-color: var(--color-secondary);
cursor: not-allowed;
}
/* 错误消息样式 */
.error-message {
background-color: var(--color-danger);
color: white;
padding: var(--spacing-md);
border-radius: 4px;
margin-top: var(--spacing-lg);
text-align: center;
}
/* 响应式设计 */
@media (max-width: 768px) {
.App-body {
flex-direction: column;
}
.App-main {
padding: var(--spacing-md);
}
.task-header {
flex-direction: column;
align-items: stretch;
}
.task-controls {
justify-content: center;
}
.form-row {
flex-direction: column;
}
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}
.task-form {
width: 95%;
padding: var(--spacing-lg);
}
}
练习题目
基础练习
- Context API练习
javascript
// 练习1:创建一个多语言Context
// 实现:语言切换、翻译功能
// 包含:英语、中文、日语支持
// 练习2:创建一个购物车Context
// 实现:添加商品、删除商品、更新数量
// 包含:价格计算、优惠券、库存检查
- Redux练习
javascript
// 练习3:创建一个博客系统Redux Store
// 实现:文章管理、用户管理、评论系统
// 包含:异步操作、错误处理、缓存
// 练习4:创建一个电商系统Redux Store
// 实现:商品管理、订单管理、支付处理
// 包含:状态持久化、离线支持
进阶练习
- 状态管理最佳实践
javascript
// 练习5:实现状态管理性能优化
// 包含:选择器优化、中间件优化、缓存策略
// 练习6:实现状态管理测试
// 包含:单元测试、集成测试、E2E测试
- 企业级应用
javascript
// 练习7:构建完整的企业级应用
// 包含:用户管理、权限控制、数据同步
// 使用:多种状态管理方案组合
学习检查点
完成标准
- 深入理解Context API高级用法
- 掌握Redux Toolkit完整生态
- 学会多种状态管理方案
- 能够构建企业级状态管理应用
- 完成所有练习题目
自我测试
- Context API和Redux的性能差异是什么?
- 什么时候使用Zustand,什么时候使用Jotai?
- 如何优化状态管理的性能?
- 企业级应用的状态管理最佳实践是什么?
扩展阅读
推荐资源
明日预告
明天我们将学习:
- React Router基础
- 路由配置和导航
- 动态路由和嵌套路由
- 路由守卫和权限控制
- 为路由学习做准备