React 18.x 学习计划 - 第五天:React状态管理

学习目标

  • 深入理解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);
  }
}

练习题目

基础练习

  1. Context API练习
javascript 复制代码
// 练习1:创建一个多语言Context
// 实现:语言切换、翻译功能
// 包含:英语、中文、日语支持

// 练习2:创建一个购物车Context
// 实现:添加商品、删除商品、更新数量
// 包含:价格计算、优惠券、库存检查
  1. Redux练习
javascript 复制代码
// 练习3:创建一个博客系统Redux Store
// 实现:文章管理、用户管理、评论系统
// 包含:异步操作、错误处理、缓存

// 练习4:创建一个电商系统Redux Store
// 实现:商品管理、订单管理、支付处理
// 包含:状态持久化、离线支持

进阶练习

  1. 状态管理最佳实践
javascript 复制代码
// 练习5:实现状态管理性能优化
// 包含:选择器优化、中间件优化、缓存策略

// 练习6:实现状态管理测试
// 包含:单元测试、集成测试、E2E测试
  1. 企业级应用
javascript 复制代码
// 练习7:构建完整的企业级应用
// 包含:用户管理、权限控制、数据同步
// 使用:多种状态管理方案组合

学习检查点

完成标准

  • 深入理解Context API高级用法
  • 掌握Redux Toolkit完整生态
  • 学会多种状态管理方案
  • 能够构建企业级状态管理应用
  • 完成所有练习题目

自我测试

  1. Context API和Redux的性能差异是什么?
  2. 什么时候使用Zustand,什么时候使用Jotai?
  3. 如何优化状态管理的性能?
  4. 企业级应用的状态管理最佳实践是什么?

扩展阅读

推荐资源

明日预告

明天我们将学习:

  • React Router基础
  • 路由配置和导航
  • 动态路由和嵌套路由
  • 路由守卫和权限控制
  • 为路由学习做准备
相关推荐
宇余10 小时前
从 useState 到 URLState:前端状态管理的另一种思路
前端·vue.js
71-310 小时前
C语言练习题——判断水仙花数(0-100000)
c语言·笔记·学习
白兰地空瓶10 小时前
🚀 10 分钟吃透 CSS position 定位!从底层原理到避坑实战,搞定所有布局难题
前端·css
FAREWELL0007510 小时前
Lua学习记录(3) --- Lua中的复杂数据类型_table
开发语言·学习·lua
hxmmm10 小时前
react性能优化两大策略bailout和eagerState
react.js
onthewaying10 小时前
在Android平台上使用Three.js优雅的加载3D模型
android·前端·three.js
冴羽11 小时前
能让 GitHub 删除泄露的苹果源码还有 8000 多个相关仓库的 DMCA 是什么?
前端·javascript·react.js
悟能不能悟11 小时前
jsp怎么拿到url参数
java·前端·javascript
Broken Arrows11 小时前
排查网络问题的一些工具的作用和常用使用方法
linux·网络·学习
程序猿小蒜11 小时前
基于SpringBoot的企业资产管理系统开发与设计
java·前端·spring boot·后端·spring