← Назад к вопросам

В каких action делал REST запросы

1.3 Junior🔥 171 комментариев
#State Management#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

REST запросы в action creators (Redux/State Management)

Ответ: В async action creators (thunks) или middleware.

Вот как я это делаю в modern React проектах:

Redux Thunk (классический подход)

// actions/users.js
import axios from 'axios';

// Async action creator (thunk)
export const fetchUsers = () => async (dispatch) => {
  // Dispatch начало загрузки
  dispatch({ type: 'USERS_LOADING' });
  
  try {
    // REST запрос ЗДЕСЬ
    const response = await axios.get('/api/v1/users');
    
    // Dispatch успешно
    dispatch({
      type: 'USERS_SUCCESS',
      payload: response.data
    });
  } catch (error) {
    // Dispatch ошибку
    dispatch({
      type: 'USERS_ERROR',
      payload: error.message
    });
  }
};

// В компоненте
import { useDispatch } from 'react-redux';

function UserList() {
  const dispatch = useDispatch();
  
  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);
  
  // render...
}

Redux Saga (для сложной логики)

// sagas/users.js
import { call, put, takeEvery } from 'redux-saga/effects';
import axios from 'axios';

function* fetchUsersSaga() {
  try {
    // REST запрос ЗДЕСЬ
    const response = yield call(axios.get, '/api/v1/users');
    
    yield put({
      type: 'USERS_SUCCESS',
      payload: response.data
    });
  } catch (error) {
    yield put({
      type: 'USERS_ERROR',
      payload: error.message
    });
  }
}

// Слушаем action
export function* userSaga() {
  yield takeEvery('FETCH_USERS', fetchUsersSaga);
}

Современный подход: RTK Query (не Redux Thunk)

// api/usersApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const usersApi = createApi({
  reducerPath: 'usersApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api/v1' }),
  endpoints: (builder) => ({
    getUsers: builder.query({
      query: () => '/users'  // REST запрос ЗДЕСЬ
    }),
    
    createUser: builder.mutation({
      query: (userData) => ({
        url: '/users',
        method: 'POST',
        body: userData
      })
    })
  })
});

export const { useGetUsersQuery, useCreateUserMutation } = usersApi;

// В компоненте
function UserList() {
  const { data: users, isLoading } = useGetUsersQuery();
  // RTK Query автоматически управляет loading, caching, errors
}

TanStack Query / React Query (рекомендую)

// hooks/useUsers.ts
import { useQuery, useMutation } from '@tanstack/react-query';
import axios from 'axios';

const api = axios.create({ baseURL: '/api/v1' });

export function useGetUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: async () => {
      // REST запрос ЗДЕСЬ
      const response = await api.get('/users');
      return response.data;
    },
    staleTime: 1000 * 60 * 5 // 5 minutes
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: async (userData) => {
      // REST запрос ЗДЕСЬ
      const response = await api.post('/users', userData);
      return response.data;
    },
    // После успеха - invalidate кэш
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    }
  });
}

// В компоненте
function UserList() {
  const { data: users, isPending, error } = useGetUsers();
  
  if (isPending) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

Правильная архитектура: слой для API

Никогда не делаю REST запросы прямо в компонентах. Всегда создаю отдельный слой:

// lib/api.ts
import axios from 'axios';

const api = axios.create({
  baseURL: '/api/v1',
  headers: {
    'Content-Type': 'application/json'
  }
});

// Add token to requests
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export default api;

// api/users.ts
import api from '@/lib/api';

export const usersApi = {
  getAll: () => api.get('/users'),
  getById: (id: string) => api.get(`/users/${id}`),
  create: (data: CreateUserInput) => api.post('/users', data),
  update: (id: string, data: UpdateUserInput) => api.patch(`/users/${id}`, data),
  delete: (id: string) => api.delete(`/users/${id}`)
};

// В экшене (Redux Thunk)
export const fetchUsers = () => async (dispatch) => {
  try {
    const response = await usersApi.getAll(); // ЗДЕСЬ используем
    dispatch({ type: 'USERS_SUCCESS', payload: response.data });
  } catch (error) {
    dispatch({ type: 'USERS_ERROR', payload: error.message });
  }
};

// ИЛИ с React Query
export function useGetUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: () => usersApi.getAll().then(r => r.data)
  });
}

Сравнение подходов

// 1. Redux Thunk - простой старый способ
// Плюсы: простой, базовый
// Минусы: много boilerplate кода, управление ошибок

// 2. Redux Saga - для сложной логики
// Плюсы: powerful, можно cancel requests
// Минусы: сложнее учить, много boilerplate

// 3. RTK Query - встроенное в Redux Toolkit
// Плюсы: удобно, встроено
// Минусы: tied to Redux, меньше flexibility

// 4. TanStack Query (РЕКОМЕНДУЮ) - лучший для большинства
// Плюсы: лучшее API, автоматическое caching, обновления
// Минусы: еще одна зависимость

// 5. SWR (от Vercel) - альтернатива
// Плюсы: простой, встроен в Next.js
// Минусы: меньше features чем React Query

Anti-pattern: REST запрос в экшене без async

// ❌ НЕПРАВИЛЬНО
export const fetchUsers = () => {
  const response = fetch('/api/users'); // ОШИБКА: не await!
  return {
    type: 'USERS_SUCCESS',
    payload: response // undefined!
  };
};

// ❌ НЕПРАВИЛЬНО - REST запрос в компоненте
function UserList() {
  const [users, setUsers] = useState([]);
  
  // ❌ Это плохо
  const response = fetch('/api/users')
    .then(r => r.json())
    .then(data => setUsers(data));
  
  return <ul>{users.map(...)}</ul>;
}

// ✅ ПРАВИЛЬНО
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    const loadUsers = async () => {
      const response = await fetch('/api/users');
      const data = await response.json();
      setUsers(data);
    };
    
    loadUsers();
  }, []); // useEffect!
  
  return <ul>{users.map(...)}</ul>;
}

Моя рекомендация для 2026

// Используй TanStack Query + Axios

// lib/api.ts
import axios from 'axios';
import { useQueryClient } from '@tanstack/react-query';

export const api = axios.create({
  baseURL: '/api/v1'
});

// hooks/useApi.ts
export function useApiQuery<T>(
  queryKey: string[],
  fn: () => Promise<T>
) {
  return useQuery({ queryKey, queryFn: fn });
}

export function useApiMutation<T>(fn: (data: any) => Promise<T>) {
  return useMutation({ mutationFn: fn });
}

// В компоненте - просто используй
const { data: users } = useApiQuery(['users'], () =>
  api.get('/users').then(r => r.data)
);

Итоги

  • Redux Thunk - для управления state
  • TanStack Query - для HTTP запросов (РЕКОМЕНДУЮ)
  • RTK Query - если уже используешь Redux
  • Всегда через async - не sync запросы
  • Отдельный слой для API - не в компонентах
  • useEffect для запросов - не прямо в render
  • Обработка ошибок - всегда try/catch
  • Caching - очень важно для performance