Zustand и TanStack Query в React Native: Практическо ръководство за управление на състоянието

Пълно практическо ръководство за Zustand и TanStack Query в React Native. Кодови примери, persist middleware, оптимистични обновявания, архитектурни препоръки и типични грешки, които да избягвате.

Въведение: Защо управлението на състоянието е толкова важно

Ако сте разработвали React Native приложения повече от няколко месеца, вероятно знаете това чувство — започвате с прост useState, после добавяте Context, после нещата се заплитат и изведнъж прекарвате повече време в дебъгване на пропсове отколкото в писане на реална логика.

Управлението на състоянието е един от най-критичните аспекти при разработката на мобилни приложения. Всяко приложение — от прост to-do лист до сложна електронна търговия — трябва да съхранява, обновява и синхронизира данни между компоненти, екрани и сървъри. Правилната стратегия може да направи разликата между бързо, отзивчиво приложение и такова, което се „закъсва" при всяко взаимодействие.

Redux дълго време беше стандартът. Но нека бъдем честни — писането на actions, reducers, selectors и middleware за всяка малка функционалност бързо става изморително. През 2026 комбинацията от Zustand за клиентско състояние и TanStack Query (бившият React Query) за сървърно състояние се утвърди като златния стандарт за модерни React Native приложения. И с основание.

В тази статия ще разгледаме подробно как да настроите и използвате тези две библиотеки заедно, с практически примери и реални архитектурни решения.

Ключовата идея: Разделение на клиентско и сървърно състояние

Преди да навлезем в конкретните библиотеки, трябва да разберем фундаменталната концепция зад тази архитектура — разделението на състоянието по произход.

Какво е клиентско състояние?

Клиентското състояние е информация, която съществува само в приложението и не зависи от външен източник. Примери:

  • Текущата тема (светла/тъмна)
  • Дали модална кутия е отворена или затворена
  • Предпочитания на потребителя за известия
  • Съдържание на кошницата за пазаруване (преди да го изпратите към сървъра)
  • Текущия изглед — списък или мрежа
  • Състоянието на автентикация (токен, потребителски данни)

Какво е сървърно състояние?

Сървърното състояние е данни, които идват от външен API или база данни. И тук нещата стават по-интересни, защото те имат съвсем различни характеристики:

  • Асинхронни са — трябва да се заредят преди да ги използвате
  • Могат да бъдат променени от други потребители или процеси
  • Изискват кеширане за добра производителност
  • Трябва да се обновяват периодично
  • Имат нужда от обработка на грешки и повторни опити

Примери: списък с продукти от API, потребителски профил, коментари, поръчки, известия от сървъра.

Защо разделението е важно

Когато смесвате двата типа в една библиотека (като Redux), кодът бързо става объркан. Ръчно управлявате кеширане, loading/error състояния, повторни опити при грешки, инвалидиране на кеша... Познато, нали?

С разделянето, Zustand се грижи за простото, синхронно клиентско състояние, а TanStack Query автоматично обработва цялата сложност на сървърните данни. Всеки си прави това, в което е добър.

Zustand: Лекотата на клиентското състояние

Какво е Zustand

Zustand (немска дума за „състояние") е минималистична библиотека за state management, създадена от екипа на Poimandres. С размер от едва ~1 KB (gzipped), тя предлага невероятно просто API базирано на хукове, без нужда от Provider компоненти или бойлерплейт код.

Честно казано, когато за пръв път видях Zustand, бях скептичен — толкова малко код за толкова мощна функционалност? Но числата говорят сами — над 30% годишен ръст в npm изтеглянията и изключително висока удовлетвореност сред разработчиците.

Инсталация

npm install zustand
# Или с yarn
yarn add zustand

Основен store

Нека създадем прост store за потребителски настройки:

// stores/useSettingsStore.ts
import { create } from 'zustand';

interface SettingsState {
  theme: 'light' | 'dark';
  language: string;
  notificationsEnabled: boolean;
  setTheme: (theme: 'light' | 'dark') => void;
  setLanguage: (language: string) => void;
  toggleNotifications: () => void;
}

const useSettingsStore = create<SettingsState>((set) => ({
  theme: 'light',
  language: 'bg',
  notificationsEnabled: true,
  setTheme: (theme) => set({ theme }),
  setLanguage: (language) => set({ language }),
  toggleNotifications: () =>
    set((state) => ({ notificationsEnabled: !state.notificationsEnabled })),
}));

export default useSettingsStore;

Забележете колко лаконичен е кодът. Няма actions, няма reducers, няма dispatch. Просто създавате store и го използвате.

Използване в компонент

Използването в компонент е направо елементарно — извиквате хука и готово:

// screens/SettingsScreen.tsx
import React from 'react';
import { View, Text, Switch, TouchableOpacity, StyleSheet } from 'react-native';
import useSettingsStore from '../stores/useSettingsStore';

export default function SettingsScreen() {
  const theme = useSettingsStore((state) => state.theme);
  const notificationsEnabled = useSettingsStore((state) => state.notificationsEnabled);
  const setTheme = useSettingsStore((state) => state.setTheme);
  const toggleNotifications = useSettingsStore((state) => state.toggleNotifications);

  return (
    <View style={[styles.container, theme === 'dark' && styles.darkBg]}>
      <Text style={theme === 'dark' ? styles.lightText : styles.darkText}>
        Тема: {theme === 'dark' ? 'Тъмна' : 'Светла'}
      </Text>
      <TouchableOpacity
        onPress={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
        style={styles.button}
      >
        <Text>Смени темата</Text>
      </TouchableOpacity>

      <View style={styles.row}>
        <Text style={theme === 'dark' ? styles.lightText : styles.darkText}>
          Известия
        </Text>
        <Switch value={notificationsEnabled} onValueChange={toggleNotifications} />
      </View>
    </View>
  );
}

Забележете как селектираме само конкретните стойности, от които компонентът се нуждае. Zustand автоматично оптимизира ре-рендерите — компонентът ще се обнови само когато избраните стойности се променят. Доста удобно.

Persist middleware: Запазване на състоянието при рестартиране

Едно от най-полезните допълнения на Zustand е persist middleware, което автоматично запазва състоянието в хранилище. За React Native използваме AsyncStorage:

npm install @react-native-async-storage/async-storage
// stores/useAuthStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

interface AuthState {
  token: string | null;
  user: { id: string; name: string; email: string } | null;
  isAuthenticated: boolean;
  login: (token: string, user: AuthState['user']) => void;
  logout: () => void;
}

const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      token: null,
      user: null,
      isAuthenticated: false,
      login: (token, user) =>
        set({ token, user, isAuthenticated: true }),
      logout: () =>
        set({ token: null, user: null, isAuthenticated: false }),
    }),
    {
      name: 'auth-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

export default useAuthStore;

Сега при рестартиране на приложението, потребителят остава автентикиран — токенът и данните му се възстановяват автоматично от AsyncStorage. Без допълнителен код.

Проследяване на хидратацията

Тук има една важна подробност. Когато използвате persist middleware с AsyncStorage, зареждането е асинхронно. Това означава, че при първия рендер store-ът може все още да не е хидратиран. Ето как да се справите с това:

// В компонент или навигация
import useAuthStore from '../stores/useAuthStore';

function AppNavigator() {
  const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
  const hasHydrated = useAuthStore.persist.hasHydrated();

  if (!hasHydrated) {
    return <SplashScreen />;
  }

  return isAuthenticated ? <MainStack /> : <AuthStack />;
}

TanStack Query: Автоматично управление на сървърни данни

Какво е TanStack Query

TanStack Query (бившият React Query) е библиотека за извличане, кеширане и синхронизиране на сървърно състояние. Тя автоматизира огромна част от работата, която преди се правеше ръчно — кеширане, дедупликация на заявки, фонови обновявания, повторни опити при грешки и управление на loading/error състояния.

Актуалната версия е TanStack Query v5 и тя на практика е стандарт за работа със сървърни данни. В модерни приложения тя управлява около 80% от всички данни.

Инсталация

npm install @tanstack/react-query

Начална конфигурация за React Native

TanStack Query изисква малко специална конфигурация за React Native — трябва да го информирате за фокуса на приложението и мрежовата свързаност:

// App.tsx
import React from 'react';
import { AppStateStatus, Platform } from 'react-native';
import { QueryClient, QueryClientProvider, focusManager } from '@tanstack/react-query';
import { useAppState } from './hooks/useAppState';
import { useOnlineManager } from './hooks/useOnlineManager';

function onAppStateChange(status: AppStateStatus) {
  if (Platform.OS !== 'web') {
    focusManager.setFocused(status === 'active');
  }
}

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 2,
      staleTime: 1000 * 60 * 5, // 5 минути
    },
  },
});

export default function App() {
  useOnlineManager();
  useAppState(onAppStateChange);

  return (
    <QueryClientProvider client={queryClient}>
      <AppNavigator />
    </QueryClientProvider>
  );
}

Хук за мрежова свързаност

Необходимо е да информирате TanStack Query за промени в мрежовата свързаност, така че да знае кога може да прави заявки:

// hooks/useOnlineManager.ts
import { useEffect } from 'react';
import { onlineManager } from '@tanstack/react-query';
import * as Network from 'expo-network';

export function useOnlineManager() {
  useEffect(() => {
    const subscription = Network.addNetworkStateListener((state) => {
      onlineManager.setOnline(!!state.isConnected);
    });
    return () => subscription.remove();
  }, []);
}

Хук за състоянието на приложението

// hooks/useAppState.ts
import { useEffect } from 'react';
import { AppState, AppStateStatus } from 'react-native';

export function useAppState(onChange: (status: AppStateStatus) => void) {
  useEffect(() => {
    const subscription = AppState.addEventListener('change', onChange);
    return () => subscription.remove();
  }, [onChange]);
}

useQuery: Извличане на данни

Основният хук за четене на данни е useQuery. Нека създадем хук за извличане на списък с продукти:

// hooks/queries/useProducts.ts
import { useQuery } from '@tanstack/react-query';
import { api } from '../utils/api';

interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
  imageUrl: string;
}

export function useProducts(category?: string) {
  return useQuery<Product[]>({
    queryKey: ['products', { category }],
    queryFn: async () => {
      const params = category ? `?category=${category}` : '';
      const response = await api.get(`/products${params}`);
      return response.data;
    },
  });
}

export function useProduct(productId: string) {
  return useQuery<Product>({
    queryKey: ['products', productId],
    queryFn: async () => {
      const response = await api.get(`/products/${productId}`);
      return response.data;
    },
    enabled: !!productId,
  });
}

Обърнете внимание на queryKey — това е уникалният идентификатор за заявката. Когато филтрирате по категория, ключът се променя и TanStack Query автоматично извлича нови данни. Кешът се управлява отделно за всяка уникална комбинация от ключове.

Използване в екран

// screens/ProductListScreen.tsx
import React from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { useProducts } from '../hooks/queries/useProducts';

export default function ProductListScreen() {
  const { data: products, isLoading, error, refetch } = useProducts();

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" />
        <Text>Зареждане на продукти...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Грешка при зареждане: {error.message}</Text>
        <TouchableOpacity onPress={() => refetch()}>
          <Text>Опитай отново</Text>
        </TouchableOpacity>
      </View>
    );
  }

  return (
    <FlatList
      data={products}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <View style={{ padding: 16, borderBottomWidth: 1, borderColor: '#eee' }}>
          <Text style={{ fontSize: 18 }}>{item.name}</Text>
          <Text style={{ color: '#666' }}>{item.price.toFixed(2)} лв.</Text>
        </View>
      )}
      onRefresh={refetch}
      refreshing={isLoading}
    />
  );
}

useMutation: Промяна на данни

За операции, които променят данни (създаване, обновяване, изтриване), използваме useMutation:

// hooks/mutations/useCreateOrder.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '../utils/api';

interface OrderData {
  products: { id: string; quantity: number }[];
  shippingAddress: string;
  paymentMethod: string;
}

export function useCreateOrder() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (orderData: OrderData) => {
      const response = await api.post('/orders', orderData);
      return response.data;
    },
    onSuccess: () => {
      // Инвалидиране на свързани заявки
      queryClient.invalidateQueries({ queryKey: ['orders'] });
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
    onError: (error) => {
      console.error('Грешка при създаване на поръчка:', error);
    },
  });
}

Обединяване на Zustand и TanStack Query: Реална архитектура

Структура на проекта

Ето препоръчителната файлова структура, която лично аз използвам в проектите си:

src/
├── stores/                    # Zustand stores (клиентско състояние)
│   ├── useAuthStore.ts
│   ├── useSettingsStore.ts
│   └── useCartStore.ts
├── hooks/
│   ├── queries/               # TanStack Query хукове за четене
│   │   ├── useProducts.ts
│   │   ├── useOrders.ts
│   │   └── useUserProfile.ts
│   ├── mutations/             # TanStack Query хукове за промяна
│   │   ├── useCreateOrder.ts
│   │   ├── useUpdateProfile.ts
│   │   └── useDeleteProduct.ts
│   ├── useAppState.ts
│   └── useOnlineManager.ts
├── screens/
├── components/
└── utils/
    ├── api.ts                 # API клиент (axios/fetch)
    └── queryClient.ts         # Конфигурация на QueryClient

Тази структура е чиста и интуитивна — веднага виждате къде какво живее.

Практически пример: Кошница с Zustand + продукти с TanStack Query

Сега идва интересната част. Нека разгледаме пълен пример, в който двете библиотеки работят заедно:

// stores/useCartStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

interface CartItem {
  productId: string;
  name: string;
  price: number;
  quantity: number;
}

interface CartState {
  items: CartItem[];
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (productId: string) => void;
  updateQuantity: (productId: string, quantity: number) => void;
  clearCart: () => void;
  getTotalPrice: () => number;
  getTotalItems: () => number;
}

const useCartStore = create<CartState>()(
  persist(
    (set, get) => ({
      items: [],
      addItem: (item) =>
        set((state) => {
          const existingItem = state.items.find(
            (i) => i.productId === item.productId
          );
          if (existingItem) {
            return {
              items: state.items.map((i) =>
                i.productId === item.productId
                  ? { ...i, quantity: i.quantity + 1 }
                  : i
              ),
            };
          }
          return { items: [...state.items, { ...item, quantity: 1 }] };
        }),
      removeItem: (productId) =>
        set((state) => ({
          items: state.items.filter((i) => i.productId !== productId),
        })),
      updateQuantity: (productId, quantity) =>
        set((state) => ({
          items: state.items.map((i) =>
            i.productId === productId ? { ...i, quantity } : i
          ),
        })),
      clearCart: () => set({ items: [] }),
      getTotalPrice: () =>
        get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
      getTotalItems: () =>
        get().items.reduce((sum, item) => sum + item.quantity, 0),
    }),
    {
      name: 'cart-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

export default useCartStore;

Свързване на Zustand филтри с TanStack Query

Една от най-мощните техники (и лично моята любима) е да използвате стойности от Zustand store като част от queryKey на TanStack Query. Така при промяна на филтрите, данните се преизвличат автоматично:

// stores/useFilterStore.ts
import { create } from 'zustand';

interface FilterState {
  category: string | null;
  priceRange: [number, number];
  searchQuery: string;
  sortBy: 'price' | 'name' | 'rating';
  setCategory: (category: string | null) => void;
  setPriceRange: (range: [number, number]) => void;
  setSearchQuery: (query: string) => void;
  setSortBy: (sort: 'price' | 'name' | 'rating') => void;
  resetFilters: () => void;
}

const useFilterStore = create<FilterState>((set) => ({
  category: null,
  priceRange: [0, 10000],
  searchQuery: '',
  sortBy: 'name',
  setCategory: (category) => set({ category }),
  setPriceRange: (priceRange) => set({ priceRange }),
  setSearchQuery: (searchQuery) => set({ searchQuery }),
  setSortBy: (sortBy) => set({ sortBy }),
  resetFilters: () =>
    set({
      category: null,
      priceRange: [0, 10000],
      searchQuery: '',
      sortBy: 'name',
    }),
}));

export default useFilterStore;
// hooks/queries/useFilteredProducts.ts
import { useQuery } from '@tanstack/react-query';
import useFilterStore from '../../stores/useFilterStore';
import { api } from '../../utils/api';

export function useFilteredProducts() {
  const filters = useFilterStore((state) => ({
    category: state.category,
    priceRange: state.priceRange,
    searchQuery: state.searchQuery,
    sortBy: state.sortBy,
  }));

  return useQuery({
    queryKey: ['products', 'filtered', filters],
    queryFn: async () => {
      const response = await api.get('/products', { params: filters });
      return response.data;
    },
    placeholderData: (previousData) => previousData,
  });
}

Тук placeholderData (заместващо keepPreviousData от v4) показва предишните данни, докато новите се зареждат. Резултатът? Плавно потребителско изживяване без мигане на екрана.

Оптимистични обновявания с useMutation

За по-бърз отклик можете да обновите UI-а преди сървърът да потвърди операцията. Това е особено важно за действия като добавяне в любими — потребителят очаква моментална реакция:

// hooks/mutations/useToggleFavorite.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '../../utils/api';

export function useToggleFavorite() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (productId: string) => {
      return api.post(`/products/${productId}/favorite`);
    },
    onMutate: async (productId) => {
      // Отмяна на текущи заявки
      await queryClient.cancelQueries({ queryKey: ['products'] });

      // Запазване на предишното състояние
      const previousProducts = queryClient.getQueryData(['products']);

      // Оптимистично обновяване
      queryClient.setQueryData(['products'], (old: any[]) =>
        old?.map((product) =>
          product.id === productId
            ? { ...product, isFavorite: !product.isFavorite }
            : product
        )
      );

      return { previousProducts };
    },
    onError: (_err, _productId, context) => {
      // Възстановяване при грешка
      queryClient.setQueryData(['products'], context?.previousProducts);
    },
    onSettled: () => {
      // Инвалидиране за сигурност
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
}

Обновяване при фокус на екрана

При навигация между екрани е добра практика да обновявате данните, когато екранът отново получи фокус. TanStack Query v5 предлага елегантен начин за това чрез свойството subscribed:

// screens/OrdersScreen.tsx
import React from 'react';
import { View, Text, FlatList } from 'react-native';
import { useIsFocused } from '@react-navigation/native';
import { useQuery } from '@tanstack/react-query';
import { api } from '../utils/api';

export default function OrdersScreen() {
  const isFocused = useIsFocused();

  const { data: orders, isLoading } = useQuery({
    queryKey: ['orders'],
    queryFn: () => api.get('/orders').then((res) => res.data),
    subscribed: isFocused,
  });

  if (isLoading) return <Text>Зареждане на поръчки...</Text>;

  return (
    <FlatList
      data={orders}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <View style={{ padding: 16 }}>
          <Text>Поръчка #{item.id}</Text>
          <Text>Статус: {item.status}</Text>
          <Text>{item.total.toFixed(2)} лв.</Text>
        </View>
      )}
    />
  );
}

Когато subscribed е false, заявката спира да получава обновявания и няма да предизвиква ре-рендери. Доста полезно за приложения с много екрани — спестява излишни мрежови заявки.

Кога какво да използвате: Бърза справка

Ето кратка справочна таблица, която можете да запазите:

  • Локално състояние на компонент (формуляри, тогъл бутони) → useState / useReducer
  • Тема, автентикация, потребителски предпочитанияZustand
  • Данни от API / база данниTanStack Query
  • Рядко променящи се стойности, достъпни навсякъде (locale) → React Context
  • Големи корпоративни приложения с екипиRedux Toolkit (все пак си има своето място)

Типични грешки, които да избягвате

1. Съхраняване на сървърни данни в Zustand

Това е най-честата грешка и аз самият съм я правил в ранните си проекти. Извличате данни от API и ги записвате в Zustand store. Не правете това — оставете TanStack Query да управлява кеширането.

// ❌ Грешно — съхраняване на API данни в Zustand
const useStore = create((set) => ({
  products: [],
  fetchProducts: async () => {
    const res = await api.get('/products');
    set({ products: res.data }); // Не правете това!
  },
}));

// ✅ Правилно — използвайте TanStack Query
function useProducts() {
  return useQuery({
    queryKey: ['products'],
    queryFn: () => api.get('/products').then((r) => r.data),
  });
}

2. Прекалено голям Zustand store

Не слагайте всичко в един store. Разделете логически свързаните данни в отделни store-ове — auth, settings, cart, ui. Всеки store трябва да има една ясна отговорност. Ако store-ът ви прехвърли 100 реда, вероятно е време да го разделите.

3. Липса на инвалидиране на кеша

Когато правите мутация, не забравяйте да инвалидирате свързаните заявки. Без invalidateQueries, кешираните данни остават остарели и потребителят няма да вижда промените. Изглежда очевидно, но се случва по-често отколкото си мислите.

4. Пренебрегване на staleTime

По подразбиране staleTime е 0 — данните се считат за остарели веднага. За данни, които не се променят често (категории, настройки), задайте по-дълъг staleTime. Ще намалите излишните заявки и ще подобрите усещането за бързина.

Производителност и оптимизация

Селективно абониране в Zustand

Винаги селектирайте само полетата, от които компонентът се нуждае. Избягвайте извличането на целия store:

// ❌ Предизвиква ре-рендер при всяка промяна в store-а
const state = useCartStore();

// ✅ Ре-рендер само при промяна на items
const items = useCartStore((state) => state.items);

// ✅ Ре-рендер само при промяна на общия брой
const totalItems = useCartStore((state) => state.getTotalItems());

Дедупликация на заявки

TanStack Query автоматично дедуплицира заявки с еднакъв queryKey. Ако три компонента на екрана извикват useProducts(), ще се направи само една HTTP заявка. Това е една от най-мощните оптимизации — и я получавате безплатно, без допълнителен код.

MMKV за по-бързо persist

За по-добра производителност при persist, помислете за замяна на AsyncStorage с react-native-mmkv. MMKV е значително по-бърз (говорим за порядъци разлика) и поддържа криптиране:

npm install react-native-mmkv
import { MMKV } from 'react-native-mmkv';
import { StateStorage } from 'zustand/middleware';

const storage = new MMKV();

const mmkvStorage: StateStorage = {
  getItem: (name) => storage.getString(name) ?? null,
  setItem: (name, value) => storage.set(name, value),
  removeItem: (name) => storage.delete(name),
};

// Използване в persist middleware
persist(storeDefinition, {
  name: 'my-store',
  storage: createJSONStorage(() => mmkvStorage),
});

DevTools за дебъгване

За TanStack Query в Expo/React Native проекти можете да инсталирате специализиран DevTools плъгин:

npm install tanstack-query-dev-tools-expo-plugin --save-dev
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useSyncQueries } from 'tanstack-query-dev-tools-expo-plugin';

const queryClient = new QueryClient();

export function App() {
  useSyncQueries({ queryClient });

  return (
    <QueryClientProvider client={queryClient}>
      {/* Компоненти */}
    </QueryClientProvider>
  );
}

За Zustand можете да използвате devtools middleware, което интегрира store-а с Redux DevTools (да, точно така — Redux DevTools работи и с Zustand):

import { devtools } from 'zustand/middleware';

const useStore = create(
  devtools(
    (set) => ({
      count: 0,
      increment: () => set(
        (state) => ({ count: state.count + 1 }),
        false,
        'increment'
      ),
    }),
    { name: 'MyStore' }
  )
);

Заключение

Комбинацията от Zustand и TanStack Query предлага елегантно и лесно за поддръжка решение за управление на състоянието в React Native. Разделяйки клиентското и сървърното състояние, получавате:

  • По-малко код — до 40% намаление на бойлерплейт в сравнение с Redux
  • По-добра производителност — автоматична оптимизация на ре-рендерите и мрежовите заявки
  • По-лесна поддръжка — ясно разделение на отговорностите
  • По-добро потребителско изживяване — автоматично кеширане, фоново обновяване и оптимистични обновявания

Тази архитектура не е просто мода — тя е резултат от години опит на общността и се е доказала в стотици продукционни приложения. Ако стартирате нов React Native проект или обмисляте миграция от Redux, дайте шанс на тази комбинация. Няма да съжалявате.

За Автора Editorial Team

Our team of expert writers and editors.