state-management-advisor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

State Management Advisor

状态管理指南

Choose and implement the right state management solution for React applications.
为React应用选择并实现合适的状态管理方案。

Quick Start

快速入门

Use local state by default, Context for simple global state, Zustand for complex client state, and TanStack Query for server state.
默认使用本地状态,简单全局状态用Context,复杂客户端状态用Zustand,服务端状态用TanStack Query。

Instructions

使用说明

Decision Tree

决策树

Start here:
  1. Is it server data (from API)? → Use TanStack Query
  2. Is it used in one component only? → Use useState
  3. Is it simple global state (theme, auth)? → Use Context
  4. Is it complex client state? → Use Zustand or Redux Toolkit
从这里开始:
  1. 是否是服务端数据(来自API)?→ 使用TanStack Query
  2. 是否仅在单个组件中使用?→ 使用useState
  3. 是否是简单全局状态(主题、权限)?→ 使用Context
  4. 是否是复杂客户端状态?→ 使用Zustand或Redux Toolkit

Local State (useState)

本地状态(useState)

For component-specific state.
Basic usage:
tsx
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
With objects:
tsx
function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });
  
  const handleChange = (field: string, value: string) => {
    setFormData(prev => ({ ...prev, [field]: value }));
  };
  
  return (
    <form>
      <input
        value={formData.name}
        onChange={e => handleChange('name', e.target.value)}
      />
    </form>
  );
}
适用于组件专属的状态管理。
基础用法:
tsx
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
处理对象:
tsx
function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });
  
  const handleChange = (field: string, value: string) => {
    setFormData(prev => ({ ...prev, [field]: value }));
  };
  
  return (
    <form>
      <input
        value={formData.name}
        onChange={e => handleChange('name', e.target.value)}
      />
    </form>
  );
}

React Context

React Context

For simple global state shared across components.
Setup:
tsx
interface AuthContextValue {
  user: User | null;
  login: (credentials: Credentials) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  
  const login = async (credentials: Credentials) => {
    const user = await api.login(credentials);
    setUser(user);
  };
  
  const logout = () => {
    setUser(null);
  };
  
  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}
Usage:
tsx
function App() {
  return (
    <AuthProvider>
      <Router />
    </AuthProvider>
  );
}

function Profile() {
  const { user, logout } = useAuth();
  return <div>{user?.name} <button onClick={logout}>Logout</button></div>;
}
Optimization - Split contexts:
tsx
// Separate frequently changing data
const ThemeContext = createContext<Theme>(null);
const ThemeUpdateContext = createContext<(theme: Theme) => void>(null);

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={theme}>
      <ThemeUpdateContext.Provider value={setTheme}>
        {children}
      </ThemeUpdateContext.Provider>
    </ThemeContext.Provider>
  );
}
适用于跨组件共享的简单全局状态。
配置:
tsx
interface AuthContextValue {
  user: User | null;
  login: (credentials: Credentials) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  
  const login = async (credentials: Credentials) => {
    const user = await api.login(credentials);
    setUser(user);
  };
  
  const logout = () => {
    setUser(null);
  };
  
  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}
使用:
tsx
function App() {
  return (
    <AuthProvider>
      <Router />
    </AuthProvider>
  );
}

function Profile() {
  const { user, logout } = useAuth();
  return <div>{user?.name} <button onClick={logout}>Logout</button></div>;
}
优化 - 拆分Context:
tsx
// 分离频繁变化的数据
const ThemeContext = createContext<Theme>(null);
const ThemeUpdateContext = createContext<(theme: Theme) => void>(null);

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={theme}>
      <ThemeUpdateContext.Provider value={setTheme}>
        {children}
      </ThemeUpdateContext.Provider>
    </ThemeContext.Provider>
  );
}

Zustand

Zustand

Lightweight global state with minimal boilerplate.
Installation:
bash
npm install zustand
Basic store:
tsx
import { create } from 'zustand';

interface CounterStore {
  count: number;
  increment: () => void;
  decrement: () => void;
}

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

// Usage
function Counter() {
  const count = useCounterStore((state) => state.count);
  const increment = useCounterStore((state) => state.increment);
  
  return <button onClick={increment}>Count: {count}</button>;
}
With async actions:
tsx
interface UserStore {
  users: User[];
  loading: boolean;
  fetchUsers: () => Promise<void>;
}

const useUserStore = create<UserStore>((set) => ({
  users: [],
  loading: false,
  fetchUsers: async () => {
    set({ loading: true });
    const users = await api.fetchUsers();
    set({ users, loading: false });
  },
}));
With middleware:
tsx
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      token: null,
      setToken: (token) => set({ token }),
    }),
    {
      name: 'auth-storage',
    }
  )
);
轻量级全局状态管理工具,样板代码极少。
安装:
bash
npm install zustand
基础Store:
tsx
import { create } from 'zustand';

interface CounterStore {
  count: number;
  increment: () => void;
  decrement: () => void;
}

const useCounterStore = create<CounterStore>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

// 使用
function Counter() {
  const count = useCounterStore((state) => state.count);
  const increment = useCounterStore((state) => state.increment);
  
  return <button onClick={increment}>Count: {count}</button>;
}
异步操作:
tsx
interface UserStore {
  users: User[];
  loading: boolean;
  fetchUsers: () => Promise<void>;
}

const useUserStore = create<UserStore>((set) => ({
  users: [],
  loading: false,
  fetchUsers: async () => {
    set({ loading: true });
    const users = await api.fetchUsers();
    set({ users, loading: false });
  },
}));
中间件:
tsx
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      token: null,
      setToken: (token) => set({ token }),
    }),
    {
      name: 'auth-storage',
    }
  )
);

Redux Toolkit

Redux Toolkit

For complex state with many interactions.
Installation:
bash
npm install @reduxjs/toolkit react-redux
Setup store:
tsx
import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Provider:
tsx
import { Provider } from 'react-redux';

function App() {
  return (
    <Provider store={store}>
      <Router />
    </Provider>
  );
}
Usage:
tsx
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <button onClick={() => dispatch(increment())}>
      Count: {count}
    </button>
  );
}
Async with createAsyncThunk:
tsx
import { createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUsers = createAsyncThunk(
  'users/fetch',
  async () => {
    const response = await api.fetchUsers();
    return response.data;
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: { data: [], loading: false },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.data = action.payload;
        state.loading = false;
      });
  },
});
适用于存在大量交互的复杂状态管理。
安装:
bash
npm install @reduxjs/toolkit react-redux
配置Store:
tsx
import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Provider:
tsx
import { Provider } from 'react-redux';

function App() {
  return (
    <Provider store={store}>
      <Router />
    </Provider>
  );
}
使用:
tsx
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <button onClick={() => dispatch(increment())}>
      Count: {count}
    </button>
  );
}
异步操作(createAsyncThunk):
tsx
import { createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUsers = createAsyncThunk(
  'users/fetch',
  async () => {
    const response = await api.fetchUsers();
    return response.data;
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: { data: [], loading: false },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.data = action.payload;
        state.loading = false;
      });
  },
});

TanStack Query (React Query)

TanStack Query(React Query)

For server state management.
Installation:
bash
npm install @tanstack/react-query
Setup:
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Router />
    </QueryClientProvider>
  );
}
Fetching data:
tsx
import { useQuery } from '@tanstack/react-query';

function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
  
  if (isLoading) return <Spinner />;
  if (error) return <Error message={error.message} />;
  
  return <UserList users={data} />;
}
Mutations:
tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateUser() {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  return (
    <button onClick={() => mutation.mutate({ name: 'John' })}>
      Create User
    </button>
  );
}
With parameters:
tsx
function User({ id }: { id: string }) {
  const { data } = useQuery({
    queryKey: ['user', id],
    queryFn: () => fetchUser(id),
  });
  
  return <div>{data?.name}</div>;
}
Optimistic updates:
tsx
const mutation = useMutation({
  mutationFn: updateUser,
  onMutate: async (newUser) => {
    await queryClient.cancelQueries({ queryKey: ['users'] });
    const previousUsers = queryClient.getQueryData(['users']);
    
    queryClient.setQueryData(['users'], (old) => [...old, newUser]);
    
    return { previousUsers };
  },
  onError: (err, newUser, context) => {
    queryClient.setQueryData(['users'], context.previousUsers);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['users'] });
  },
});
用于服务端状态管理。
安装:
bash
npm install @tanstack/react-query
配置:
tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Router />
    </QueryClientProvider>
  );
}
数据获取:
tsx
import { useQuery } from '@tanstack/react-query';

function Users() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
  
  if (isLoading) return <Spinner />;
  if (error) return <Error message={error.message} />;
  
  return <UserList users={data} />;
}
数据变更:
tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateUser() {
  const queryClient = useQueryClient();
  
  const mutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  return (
    <button onClick={() => mutation.mutate({ name: 'John' })}>
      Create User
    </button>
  );
}
带参数:
tsx
function User({ id }: { id: string }) {
  const { data } = useQuery({
    queryKey: ['user', id],
    queryFn: () => fetchUser(id),
  });
  
  return <div>{data?.name}</div>;
}
乐观更新:
tsx
const mutation = useMutation({
  mutationFn: updateUser,
  onMutate: async (newUser) => {
    await queryClient.cancelQueries({ queryKey: ['users'] });
    const previousUsers = queryClient.getQueryData(['users']);
    
    queryClient.setQueryData(['users'], (old) => [...old, newUser]);
    
    return { previousUsers };
  },
  onError: (err, newUser, context) => {
    queryClient.setQueryData(['users'], context.previousUsers);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['users'] });
  },
});

Jotai

Jotai

Atomic state management.
Installation:
bash
npm install jotai
Basic atoms:
tsx
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
Derived atoms:
tsx
const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);

function Display() {
  const [doubleCount] = useAtom(doubleCountAtom);
  return <div>Double: {doubleCount}</div>;
}
Async atoms:
tsx
const usersAtom = atom(async () => {
  const response = await fetch('/api/users');
  return response.json();
});

function Users() {
  const [users] = useAtom(usersAtom);
  return <UserList users={users} />;
}
原子化状态管理工具。
安装:
bash
npm install jotai
基础原子:
tsx
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}
派生原子:
tsx
const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);

function Display() {
  const [doubleCount] = useAtom(doubleCountAtom);
  return <div>Double: {doubleCount}</div>;
}
异步原子:
tsx
const usersAtom = atom(async () => {
  const response = await fetch('/api/users');
  return response.json();
});

function Users() {
  const [users] = useAtom(usersAtom);
  return <UserList users={users} />;
}

Comparison Matrix

方案对比矩阵

SolutionBest ForProsCons
useStateLocal stateSimple, built-inLimited to component
ContextSimple global stateBuilt-in, no depsCan cause re-renders
ZustandComplex client stateMinimal, fastSmaller ecosystem
Redux ToolkitLarge apps, teamsPowerful, DevToolsVerbose, learning curve
TanStack QueryServer stateCaching, syncNot for client state
JotaiAtomic stateFine-grained, modernSmaller ecosystem
方案最佳适用场景优点缺点
useState本地状态简单、内置仅限单个组件使用
Context简单全局状态内置、无依赖可能导致不必要重渲染
Zustand复杂客户端状态轻量、快速生态系统较小
Redux Toolkit大型应用、团队协作功能强大、支持DevTools代码繁琐、学习曲线陡
TanStack Query服务端状态缓存、同步机制完善不适用于客户端状态
Jotai原子化状态细粒度控制、现代化生态系统较小

Common Patterns

常见模式

Combining Solutions

方案组合使用

TanStack Query + Zustand:
tsx
// Server state with TanStack Query
const { data: users } = useQuery(['users'], fetchUsers);

// Client state with Zustand
const selectedUserId = useStore((state) => state.selectedUserId);
Context + TanStack Query:
tsx
function AuthProvider({ children }) {
  const { data: user } = useQuery(['me'], fetchCurrentUser);
  
  return (
    <AuthContext.Provider value={{ user }}>
      {children}
    </AuthContext.Provider>
  );
}
TanStack Query + Zustand:
tsx
// 用TanStack Query管理服务端状态
const { data: users } = useQuery(['users'], fetchUsers);

// 用Zustand管理客户端状态
const selectedUserId = useStore((state) => state.selectedUserId);
Context + TanStack Query:
tsx
function AuthProvider({ children }) {
  const { data: user } = useQuery(['me'], fetchCurrentUser);
  
  return (
    <AuthContext.Provider value={{ user }}>
      {children}
    </AuthContext.Provider>
  );
}

Form State

表单状态

Controlled with useState:
tsx
function Form() {
  const [values, setValues] = useState({ name: '', email: '' });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    api.submit(values);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={values.name}
        onChange={e => setValues(prev => ({ ...prev, name: e.target.value }))}
      />
    </form>
  );
}
With form library:
tsx
import { useForm } from 'react-hook-form';

function Form() {
  const { register, handleSubmit } = useForm();
  
  const onSubmit = (data) => api.submit(data);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
    </form>
  );
}
用useState实现受控表单:
tsx
function Form() {
  const [values, setValues] = useState({ name: '', email: '' });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    api.submit(values);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={values.name}
        onChange={e => setValues(prev => ({ ...prev, name: e.target.value }))}
      />
    </form>
  );
}
结合表单库:
tsx
import { useForm } from 'react-hook-form';

function Form() {
  const { register, handleSubmit } = useForm();
  
  const onSubmit = (data) => api.submit(data);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
    </form>
  );
}

Troubleshooting

问题排查

Context causing re-renders:
  • Split into multiple contexts
  • Use useMemo for context value
  • Consider Zustand instead
Stale data in TanStack Query:
  • Adjust staleTime and cacheTime
  • Use refetchInterval for polling
  • Invalidate queries after mutations
Redux boilerplate:
  • Use Redux Toolkit (not legacy Redux)
  • Use createSlice for reducers
  • Use createAsyncThunk for async
State updates not reflecting:
  • Check if mutating state directly
  • Use functional updates
  • Verify dependencies in useEffect
Context导致不必要重渲染:
  • 拆分为多个Context
  • 对Context值使用useMemo
  • 考虑替换为Zustand
TanStack Query中数据过期:
  • 调整staleTime和cacheTime
  • 使用refetchInterval实现轮询
  • 数据变更后失效对应查询
Redux样板代码过多:
  • 使用Redux Toolkit(而非传统Redux)
  • 用createSlice创建reducer
  • 用createAsyncThunk处理异步操作
状态更新未生效:
  • 检查是否直接修改了状态
  • 使用函数式更新
  • 验证useEffect中的依赖项