Підходи до управління станом у React
Управління станом у React
Вибір правильного підходу до управління станом залежить від того, який тип стану ви управляєте і наскільки широко він розподілений. React пропонує вбудовані варіанти, а екосистема надає потужні зовнішні бібліотеки.
Типи стану
| Тип | Приклади | Найкращий підхід |
|---|---|---|
| Локальний/UI стан | Введення форм, перемикачі, модальні вікна | useState, useReducer |
| Спільний стан | Тема, аутентифікація, кошик | Context, Zustand, Redux |
| Серверний стан | Дані API, кеш | TanStack Query, SWR |
| Стан URL | Фільтри, пагінація | Параметри URL, параметри пошуку |
| Стан форми | Складна валідація форм | React Hook Form, Formik |
Вбудований: useState
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}Найкраще підходить для: Простого, локального стану компонента.
Вбудований: useReducer
type Action = { type: "increment" } | { type: "decrement" } | { type: "reset" };
function reducer(state: number, action: Action): number {
switch (action.type) {
case "increment": return state + 1;
case "decrement": return state - 1;
case "reset": return 0;
}
}
function Counter() {
const [count, dispatch] = useReducer(reducer, 0);
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
);
}Найкраще підходить для: Складного локального стану з кількома діями.
Вбудований: Context API
const ThemeContext = createContext<"light" | "dark">("light");
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<"light" | "dark">("light");
return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click me</button>;
}Найкраще підходить для: Рідко змінюваних глобальних значень (тема, локаль, аутентифікація).
⚠️ Обмеження: всі споживачі повторно рендеряться, коли значення контексту змінюється.
Zustand (Легкий магазин)
import { create } from "zustand";
interface CartStore {
items: Product[];
addItem: (item: Product) => void;
removeItem: (id: string) => void;
total: () => number;
}
const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (item) => set(state => ({ items: [...state.items, item] })),
removeItem: (id) => set(state => ({
items: state.items.filter(i => i.id !== id)
})),
total: () => get().items.reduce((sum, i) => sum + i.price, 0),
}));
// Використання — підписується лише на те, що читає
function CartCount() {
const count = useCartStore(state => state.items.length);
return <span>{count}</span>;
}Найкраще підходить для: Простого до середнього спільного стану. Мінімальний шаблон, відмінна продуктивність.
Redux Toolkit
import { createSlice, configureStore } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 },
},
});
const store = configureStore({ reducer: { counter: counterSlice.reducer } });Найкраще підходить для: Великих додатків зі складним станом, проміжним програмним забезпеченням, інструментами розробника.
TanStack Query (Серверний стан)
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then(r => r.json()),
});
if (isLoading) return <Spinner />;
return <UserList users={data} />;
}Найкраще підходить для: Серверного стану — кешування, повторне отримання у фоновому режимі, оптимістичні оновлення, пагінація.
Посібник з прийняття рішень
Чи є це локальним для ОДНОГО компонента?
→ useState / useReducer
Чи ділиться це ДЕЯКИМИ сусідніми компонентами?
→ Підняття стану вгору
Чи є це глобальним, але змінюється РІДКО (тема, аутентифікація)?
→ Context API
Чи є це глобальним і змінюється ЧАСТО?
→ Zustand (простий) або Redux Toolkit (складний)
Чи є це даними з API?
→ TanStack Query / SWRВажливо:
Немає єдиного найкращого рішення — використовуйте правильний інструмент для кожного типу стану. Почніть з useState, підніміть стан вгору, коли це необхідно, додайте Context для глобальних значень, використовуйте TanStack Query для даних сервера, і звертайтеся до Zustand або Redux лише коли це дійсно необхідно. Уникайте розміщення всього в глобальному стані.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.