Глобальное состояние
Когда состояние нужно многим экранам, используют стор.
Когда нужно
- Пользователь (авторизация)
- Тема
- Корзина
- Настройки приложения
- Кеш избранного
Варианты
- Zustand — минималистичный
- Redux Toolkit — мощный, но много boilerplate
- Jotai — атомарный
- Context + useReducer — без библиотек
Zustand
npm install zustand
import { create } from 'zustand';
type CartStore = {
items: CartItem[];
add: (item: CartItem) => void;
remove: (id: number) => void;
clear: () => void;
total: () => number;
};
export const useCartStore = create<CartStore>((set, get) => ({
items: [],
add: (item) => set((s) => ({ items: [...s.items, item] })),
remove: (id) => set((s) => ({ items: s.items.filter((i) => i.id !== id) })),
clear: () => set({ items: [] }),
total: () => get().items.reduce((sum, i) => sum + i.price, 0),
}));
Использование:
function CartButton({ item }: { item: CartItem }) {
const add = useCartStore((s) => s.add);
return <Pressable onPress={() => add(item)}><Text>В корзину</Text></Pressable>;
}
function CartBadge() {
const count = useCartStore((s) => s.items.length);
return <Text>{count}</Text>;
}
Persist в Zustand
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
// ...
}),
{
name: 'cart',
storage: createJSONStorage(() => AsyncStorage),
}
)
);
Корзина сохраняется между запусками.
Redux Toolkit
npm install @reduxjs/toolkit react-redux
import { createSlice, configureStore } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [] as CartItem[] },
reducers: {
add: (state, action) => { state.items.push(action.payload); },
remove: (state, action) => { state.items = state.items.filter((i) => i.id !== action.payload); },
},
});
export const { add, remove } = cartSlice.actions;
export const store = configureStore({
reducer: { cart: cartSlice.reducer },
});
import { Provider, useSelector, useDispatch } from 'react-redux';
<Provider store={store}>
<App />
</Provider>
function Cart() {
const items = useSelector((s: RootState) => s.cart.items);
const dispatch = useDispatch();
return <>{items.map((i) => <Text key={i.id}>{i.name}</Text>)}</>;
}
Context + useReducer
Без библиотек:
const CartStateContext = createContext<CartItem[] | null>(null);
const CartDispatchContext = createContext<Dispatch<Action> | null>(null);
function CartProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(reducer, []);
return (
<CartStateContext.Provider value={state}>
<CartDispatchContext.Provider value={dispatch}>
{children}
</CartDispatchContext.Provider>
</CartStateContext.Provider>
);
}
Что выбрать
- Zustand — 80% случаев, минималистичный
- Redux Toolkit — большой проект, devtools, middleware
- Jotai — атомарное состояние, много независимых частей
- Context — простые случаи, одна-две переменных
Серверное состояние ≠ глобальное
Серверные данные — в TanStack Query. Глобальное состояние — только клиентское: корзина, тема, настройки.
Итоги
- Zustand для большинства случаев
- Redux для энтерпрайза
- Persist для сохранения
- Context без библиотек
- Серверное состояние — в TanStack Query