Logo Craft Homelab Docs Контакты Telegram
React Native: состояние и данные — Zustand и React Query
Mon Dec 22 2025

Управление состоянием в React Native

Управление состоянием — ключевой аспект React Native разработки. Рассмотрим различные подходы. От простого useState до Redux и Zustand — каждый подход имеет свои use cases и компромиссы.

Local State

Локальное состояние используется для данных, которые не выходят за пределы компонента.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <View>
      <Text>{count}</Text>
      <Button title="+" onPress={() => setCount(c => c + 1)} />
    </View>
  );
}

Для простых случаев локального состояния useState достаточно. Но для общих данных нужны другие решения.

Context

Context позволяет передавать данные через дерево компонентов без пропсов.

// ThemeContext.tsx
import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

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

export function useTheme() {
  return useContext(ThemeContext);
}

// Использование
function ThemedButton() {
  const { theme, setTheme } = useTheme();
  
  return (
    <Button 
      title={theme}
      onPress={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}
    />
  );
}

Redux Toolkit

Redux Toolkit — официальная библиотека для Redux с упрощённым API.

npm install @reduxjs/toolkit react-redux

Redux использует однонаправленный поток данных: компоненты диспатчат действия, редюсеры обновляют состояние.

Redux Toolkit упрощает настройку Redux, автоматически создавая экшены и редюсеры:

// store.ts
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; },
    incrementByAmount: (state, action) => { state.value += action.payload; },
  },
});

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

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

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// Использование
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './store';

function Counter() {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useDispatch<AppDispatch>();
  
  return (
    <Button title={count} onPress={() => dispatch(increment())} />
  );
}
// Provider
import { Provider } from 'react-redux';
import { store } from './store';

function App() {
  return (
    <Provider store={store}>
      <MainScreen />
    </Provider>
  );
}

Zustand

npm install zustand
// store.ts
import { create } from 'zustand';

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

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

// Использование
function Counter() {
  const { count, increment, decrement } = useStore();
  
  return (
    <View>
      <Text>{count}</Text>
      <Button title="+" onPress={increment} />
    </View>
  );
}

React Query

npm install @tanstack/react-query
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UserList />
    </QueryClientProvider>
  );
}

function UserList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: async () => {
      const res = await fetch('https://api.example.com/users');
      return res.json();
    },
  });
  
  if (isLoading) return <Text>Loading...</Text>;
  if (error) return <Text>Error: {error.message}</Text>;
  
  return (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => <Text>{item.name}</Text>}
    />
  );
}

AsyncStorage

npm install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';

// Сохранение
await AsyncStorage.setItem('user', JSON.stringify(user));

// Чтение
const userJson = await AsyncStorage.getItem('user');
const user = userJson ? JSON.parse(userJson) : null;

// Удаление
await AsyncStorage.removeItem('user');

Persist с Zustand

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

const useStore = create(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'my-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

Заключение

Выбор инструмента зависит от сложности приложения: local state для простых компонентов, Context для theme/locale, Zustand/Redux для глобального состояния, React Query для данных с сервера.