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基础
  • 路由配置和导航
  • 动态路由和嵌套路由
  • 路由守卫和权限控制
  • 为路由学习做准备
相关推荐
-睡到自然醒~3 小时前
[go 面试] 前端请求到后端API的中间件流程解析
前端·中间件·面试
洛卡卡了3 小时前
Sentry 都不想接,这锅还让我背?这xx工作我不要了!
前端·架构
咖啡の猫3 小时前
Vue 实例生命周期
前端·vue.js·okhttp
baole9633 小时前
YOLOv4简单基础学习
学习·yolo·目标跟踪
JNU freshman3 小时前
vue 之 import 的语法
前端·javascript·vue.js
剑亦未配妥3 小时前
Vue 2 响应式系统常见问题与解决方案(包含_demo以下划线开头命名的变量导致响应式丢失问题)
前端·javascript·vue.js
凉柚ˇ3 小时前
Vue图片压缩方案
前端·javascript·vue.js
慧一居士3 小时前
vue 中 directive 作用,使用场景和使用示例
前端
慧一居士4 小时前
vue 中 file-saver 功能介绍,使用场景,使用示例
前端