用 AI 快速上手现代 React 状态管理方案

解决前端状态混乱痛点:当需要构建全局状态、处理服务端数据缓存或迁移旧版 Redux 时,该技能能自动匹配最佳工具(Zustand/Redux/RTK),输出类型安全且遵循最佳实践的初始化代码。

为什么需要这个技能

React 应用随着功能增加,状态管理方案的选择至关重要。 新手常混淆组件内部状态(useState)与全局状态(Redux/Zustand)。 此外,如何处理异步服务器数据缓存也是常见难题。

本技能通过分析你的项目规模和复杂度,自动推荐 Zustand(轻量)、Redux Toolkit(生态成熟)或 Jotai(原子化)等方案,并生成包含持久化、优化更新和类型推导的完整代码模板,避免重复造轮子。

适用场景

  • 正在初始化一个新的 React 项目,需要在配置阶段选定状态库。
  • 需要将原有的 Redux store 迁移到更现代的 Zustand 或 Jotai。
  • 需要区分处理服务端 API 数据(React Query)与客户端 UI 状态。
  • 想要实现乐观更新(Optimistic Updates)或复杂的原子状态逻辑。

核心工作流

  1. 评估项目规模与需求

    • 小应用或简单状态:推荐使用 ZustandJotai
    • 大型复杂应用:推荐 Redux Toolkit 搭配 RTK Query
    • 重型服务器交互:必须引入 React QuerySWR
  2. 选择工具并生成架构

    • 根据 Selection Criteria 自动生成对应的 Store 结构。
    • 自动生成 TypeScript 类型定义,确保 RootStateAppDispatch 的类型安全。
  3. 实施具体模式

    • 使用 Zustand 快速搭建原子状态或持久化存储。
    • 使用 React Query 处理 useQueryuseMutation,自动处理缓存失效和回退。
    • 实现 Jotai 的原子状态(Atom)和派生状态(Derived Atom)。

代码示例

Zustand (最简洁)

适用于简单状态,开箱即用,支持持久化。

// store/useStore.ts
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'

interface AppState {
  user: User | null
  theme: 'light' | 'dark'
  setUser: (user: User | null) => void
  toggleTheme: () => void
}

export const useStore = create<AppState>()(
  devtools(
    persist(
      (set) => ({
        user: null,
        theme: 'light',
        setUser: (user) => set({ user }),
        toggleTheme: () => set((state) => ({
          theme: state.theme === 'light' ? 'dark' : 'light'
        })),
      }),
      { name: 'app-storage' }
    )
  )
)

// Usage in component
function Header() {
  const { user, theme, toggleTheme } = useStore()
  return (
    <header className={theme}>
      {user?.name}
      <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
  )
}

Redux Toolkit with TypeScript

适用于大型应用,包含完善的切片(Slice)和类型推导。

// store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import userReducer from './slices/userSlice'
import cartReducer from './slices/cartSlice'

export const store = configureStore({
  reducer: {
    user: userReducer,
    cart: cartReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: ['persist/PERSIST'],
      },
    }),
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

// Typed hooks
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
// store/slices/userSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'

interface User {
  id: string
  email: string
  name: string
}

interface UserState {
  current: User | null
  status: 'idle' | 'loading' | 'succeeded' | 'failed'
  error: string | null
}

const initialState: UserState = {
  current: null,
  status: 'idle',
  error: null,
}

export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId: string, { rejectWithValue }) => {
    try {
      const response = await fetch(`/api/users/${userId}`)
      if (!response.ok) throw new Error('Failed to fetch user')
      return await response.json()
    } catch (error) {
      return rejectWithValue((error as Error).message)
    }
  }
)

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setUser: (state, action: PayloadAction<User>) => {
      state.current = action.payload
      state.status = 'succeeded'
    },
    clearUser: (state) => {
      state.current = null
      state.status = 'idle'
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.status = 'loading'
        state.error = null
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.status = 'succeeded'
        state.current = action.payload
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.status = 'failed'
        state.error = action.payload as string
      })
  },
})

export const { setUser, clearUser } = userSlice.actions
export default userSlice.reducer

Zustand with Slices (可扩展)

通过组合函数将多个 Slice 整合到一个 Store。

// store/slices/createUserSlice.ts
import { StateCreator } from 'zustand'

export interface UserSlice {
  user: User | null
  isAuthenticated: boolean
  login: (credentials: Credentials) => Promise<void>
  logout: () => void
}

export const createUserSlice: StateCreator<
  UserSlice & CartSlice, // Combined store type
  [],
  [],
  UserSlice
> = (set, get) => ({
  user: null,
  isAuthenticated: false,
  login: async (credentials) => {
    const user = await authApi.login(credentials)
    set({ user, isAuthenticated: true })
  },
  logout: () => {
    set({ user: null, isAuthenticated: false })
    // Can access other slices
    // get().clearCart()
  },
})

React Query for Server State

分离服务端状态,自动处理缓存、重试和乐观更新。

// hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

// Query keys factory
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
}

// Fetch hook
export function useUsers(filters: UserFilters) {
  return useQuery({
    queryKey: userKeys.list(filters),
    queryFn: () => fetchUsers(filters),
    staleTime: 5 * 60 * 1000, // 5 minutes
    gcTime: 30 * 60 * 1000, // 30 minutes (formerly cacheTime)
  })
}

// Mutation with optimistic update
export function useUpdateUser() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: updateUser,
    onMutate: async (newUser) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: userKeys.detail(newUser.id) })

      // Snapshot previous value
      const previousUser = queryClient.getQueryData(userKeys.detail(newUser.id))

      // Optimistically update
      queryClient.setQueryData(userKeys.detail(newUser.id), newUser)

      return { previousUser }
    },
    onError: (err, newUser, context) => {
      // Rollback on error
      queryClient.setQueryData(
        userKeys.detail(newUser.id),
        context?.previousUser
      )
    },
    onSettled: (data, error, variables) => {
      // Refetch after mutation
      queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) })
    },
  })
}

最佳实践

  • 状态就近原则 (Colocate state):状态尽量靠近使用它的组件。
  • 使用选择器 (Selectors):配合 Zustand 或 Redux 使用选择器防止不必要的重渲染。
  • 规范化数据 (Normalize data):扁平化嵌套结构以便于更新。
  • 类型覆盖 (Type everything):使用 TypeScript 防止运行时错误。
  • 不要过度全局化:非核心交互逻辑不必放入全局 Store。

下载和安装

下载 react-state-management 中文版 Skill ZIP

解压后将目录放入你的 AI 工具 skills 文件夹,重启工具后即可使用。

你可能还需要

暂无推荐