Loading...
Loading...
Compare original and translation side by side
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}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>
);
}function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}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>
);
}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;
}function App() {
return (
<AuthProvider>
<Router />
</AuthProvider>
);
}
function Profile() {
const { user, logout } = useAuth();
return <div>{user?.name} <button onClick={logout}>Logout</button></div>;
}// 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>
);
}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;
}function App() {
return (
<AuthProvider>
<Router />
</AuthProvider>
);
}
function Profile() {
const { user, logout } = useAuth();
return <div>{user?.name} <button onClick={logout}>Logout</button></div>;
}// 分离频繁变化的数据
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>
);
}npm install zustandimport { 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>;
}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 });
},
}));import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
token: null,
setToken: (token) => set({ token }),
}),
{
name: 'auth-storage',
}
)
);npm install zustandimport { 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>;
}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 });
},
}));import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
token: null,
setToken: (token) => set({ token }),
}),
{
name: 'auth-storage',
}
)
);npm install @reduxjs/toolkit react-reduximport { 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;import { Provider } from 'react-redux';
function App() {
return (
<Provider store={store}>
<Router />
</Provider>
);
}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>
);
}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;
});
},
});npm install @reduxjs/toolkit react-reduximport { 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;import { Provider } from 'react-redux';
function App() {
return (
<Provider store={store}>
<Router />
</Provider>
);
}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>
);
}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;
});
},
});npm install @tanstack/react-queryimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
}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} />;
}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>
);
}function User({ id }: { id: string }) {
const { data } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
});
return <div>{data?.name}</div>;
}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'] });
},
});npm install @tanstack/react-queryimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
}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} />;
}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>
);
}function User({ id }: { id: string }) {
const { data } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
});
return <div>{data?.name}</div>;
}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'] });
},
});npm install jotaiimport { atom, useAtom } from 'jotai';
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);
function Display() {
const [doubleCount] = useAtom(doubleCountAtom);
return <div>Double: {doubleCount}</div>;
}const usersAtom = atom(async () => {
const response = await fetch('/api/users');
return response.json();
});
function Users() {
const [users] = useAtom(usersAtom);
return <UserList users={users} />;
}npm install jotaiimport { atom, useAtom } from 'jotai';
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);
function Display() {
const [doubleCount] = useAtom(doubleCountAtom);
return <div>Double: {doubleCount}</div>;
}const usersAtom = atom(async () => {
const response = await fetch('/api/users');
return response.json();
});
function Users() {
const [users] = useAtom(usersAtom);
return <UserList users={users} />;
}| Solution | Best For | Pros | Cons |
|---|---|---|---|
| useState | Local state | Simple, built-in | Limited to component |
| Context | Simple global state | Built-in, no deps | Can cause re-renders |
| Zustand | Complex client state | Minimal, fast | Smaller ecosystem |
| Redux Toolkit | Large apps, teams | Powerful, DevTools | Verbose, learning curve |
| TanStack Query | Server state | Caching, sync | Not for client state |
| Jotai | Atomic state | Fine-grained, modern | Smaller ecosystem |
| 方案 | 最佳适用场景 | 优点 | 缺点 |
|---|---|---|---|
| useState | 本地状态 | 简单、内置 | 仅限单个组件使用 |
| Context | 简单全局状态 | 内置、无依赖 | 可能导致不必要重渲染 |
| Zustand | 复杂客户端状态 | 轻量、快速 | 生态系统较小 |
| Redux Toolkit | 大型应用、团队协作 | 功能强大、支持DevTools | 代码繁琐、学习曲线陡 |
| TanStack Query | 服务端状态 | 缓存、同步机制完善 | 不适用于客户端状态 |
| Jotai | 原子化状态 | 细粒度控制、现代化 | 生态系统较小 |
// Server state with TanStack Query
const { data: users } = useQuery(['users'], fetchUsers);
// Client state with Zustand
const selectedUserId = useStore((state) => state.selectedUserId);function AuthProvider({ children }) {
const { data: user } = useQuery(['me'], fetchCurrentUser);
return (
<AuthContext.Provider value={{ user }}>
{children}
</AuthContext.Provider>
);
}// 用TanStack Query管理服务端状态
const { data: users } = useQuery(['users'], fetchUsers);
// 用Zustand管理客户端状态
const selectedUserId = useStore((state) => state.selectedUserId);function AuthProvider({ children }) {
const { data: user } = useQuery(['me'], fetchCurrentUser);
return (
<AuthContext.Provider value={{ user }}>
{children}
</AuthContext.Provider>
);
}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>
);
}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>
);
}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>
);
}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>
);
}