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

Что в RTK Query облегчает разработку и создание запросов на сервер?

2.2 Middle🔥 171 комментариев
#JavaScript Core

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

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

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

RTK Query - облегчение разработки API запросов

RTK Query - это мощная библиотека для управления серверным состоянием в Redux, которая значительно упрощает создание и управление API запросами. Она построена на основе Redux Toolkit и решает множество распространённых проблем при работе с асинхронными данными.

1. Автоматическое кеширование

RTK Query автоматически кеширует результаты запросов, что исключает необходимость ручного управления кешем:

// src/services/api.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
  endpoints: (builder) => ({
    getUser: builder.query({
      query: (userId) => `/users/${userId}`
      // Результат автоматически кешируется
    }),
    getPost: builder.query({
      query: (postId) => `/posts/${postId}`
    })
  })
});

export const { useGetUserQuery, useGetPostQuery } = apiSlice;

// В компоненте
function UserProfile({ userId }) {
  const { data: user, isLoading } = useGetUserQuery(userId);
  // При переходе на другого пользователя и возврате - данные из кеша
}

2. Реактивные хуки

RTK Query предоставляет хуки, которые автоматически управляют состоянием запроса:

function PostsList() {
  const { data: posts, isLoading, isError, error, isFetching } = useGetPostsQuery();

  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error.message}</div>;

  return (
    <div>
      {isFetching && <div>Refreshing...</div>}
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

3. Автоматическая синхронизация данных (Mutations)

Мутации позволяют обновлять серверные данные и автоматически инвалидировать связанные кеши:

const apiSlice = createApi({
  endpoints: (builder) => ({
    getUsers: builder.query({
      query: () => '/users',
      providesTags: ['User']
    }),
    createUser: builder.mutation({
      query: (userData) => ({
        url: '/users',
        method: 'POST',
        body: userData
      }),
      invalidatesTags: ['User']
      // После создания пользователя - автоматически перезагрузит список
    }),
    updateUser: builder.mutation({
      query: ({ id, ...patch }) => ({
        url: `/users/${id}`,
        method: 'PATCH',
        body: patch
      }),
      invalidatesTags: (result, error, arg) => [{ type: 'User', id: arg.id }]
      // Инвалидирует конкретного пользователя
    })
  })
});

export const { useGetUsersQuery, useCreateUserMutation, useUpdateUserMutation } = apiSlice;

// В компоненте
function UserForm() {
  const [createUser, { isLoading }] = useCreateUserMutation();

  const handleSubmit = async (userData) => {
    await createUser(userData).unwrap();
    // useGetUsersQuery автоматически перезагрузит данные
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

4. Управление тегами и инвалидацией кеша

Теги позволяют контролировать, какой кеш инвалидировать при мутации:

const apiSlice = createApi({
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
      providesTags: ['Post']
    }),
    getPostById: builder.query({
      query: (id) => `/posts/${id}`,
      providesTags: (result, error, arg) => [{ type: 'Post', id: arg }]
    }),
    createPost: builder.mutation({
      query: (postData) => ({
        url: '/posts',
        method: 'POST',
        body: postData
      }),
      invalidatesTags: ['Post']
      // Инвалидирует все запросы с тегом 'Post'
    }),
    deletePost: builder.mutation({
      query: (id) => ({
        url: `/posts/${id}`,
        method: 'DELETE'
      }),
      invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg }]
      // Инвалидирует конкретный пост
    })
  })
});

5. Параметризованные запросы

Исползуйте параметры для фильтрации и поиска:

const apiSlice = createApi({
  endpoints: (builder) => ({
    searchUsers: builder.query({
      query: ({ name, limit = 10, offset = 0 }) => 
        `/users?name=${name}&limit=${limit}&offset=${offset}`,
      providesTags: (result) => 
        result 
          ? [
              ...result.map(({ id }) => ({ type: 'User', id })),
              { type: 'User', id: 'PARTIAL-LIST' }
            ]
          : [{ type: 'User', id: 'PARTIAL-LIST' }]
    })
  })
});

// В компоненте
function SearchUsers({ searchTerm }) {
  const { data: users, isLoading } = useSearchUsersQuery(
    { name: searchTerm },
    { skip: !searchTerm } // Не делать запрос, пока нет поискового терма
  );

  return <div>{users?.map(user => <div key={user.id}>{user.name}</div>)}</div>;
}

6. Трансформация ответов

Обработайте ответ с сервера перед сохранением в кеш:

const apiSlice = createApi({
  endpoints: (builder) => ({
    getUsers: builder.query({
      query: () => '/users',
      transformResponse: (response) => {
        // response может быть { data: [...], meta: {...} }
        return response.data.map(user => ({
          ...user,
          displayName: `${user.firstName} ${user.lastName}`
        }));
      },
      providesTags: (result) => result
        ? result.map(({ id }) => ({ type: 'User', id }))
        : []
    })
  })
});

7. Обработка ошибок

Унифицированная обработка ошибок:

function UserProfile({ userId }) {
  const { data, error, isError } = useGetUserQuery(userId);

  if (isError) {
    if (error.status === 404) {
      return <div>User not found</div>;
    }
    if (error.status === 401) {
      return <div>Please log in</div>;
    }
    return <div>Error: {error.data?.message}</div>;
  }

  return <div>{data?.name}</div>;
}

// Глобальная обработка ошибок
const apiSlice = createApi({
  baseQuery: async (args, api, extraOptions) => {
    const result = await fetchBaseQuery({
      baseUrl: 'https://api.example.com'
    })(args, api, extraOptions);

    if (result.error?.status === 401) {
      // Обработать 401 ошибку
      api.dispatch(logOut());
    }

    return result;
  },
  endpoints: (builder) => ({})
});

8. Условные запросы и отсрочка

function Dashboard({ userId }) {
  // Не делать запрос, пока userId не определен
  const { data: user } = useGetUserQuery(userId, { skip: !userId });

  // Отсрочить запрос на определённое время
  const { data: posts } = useGetPostsQuery(
    { userId },
    { pollingInterval: 30000 } // Обновлять каждые 30 секунд
  );

  return <div>{user?.name} - {posts?.length} posts</div>;
}

9. Оптимистичные обновления

const [updatePost] = useUpdatePostMutation();
const queryApi = apiSlice.util;

const handleUpdate = async (id, newData) => {
  // Сразу обновляем кеш
  const patchResult = queryApi.updateQueryData('getPostById', id, (draft) => {
    Object.assign(draft, newData);
  });

  try {
    await updatePost({ id, ...newData }).unwrap();
  } catch (err) {
    // Откатить изменения при ошибке
    patchResult.undo();
  }
};

10. Серверизация состояния (SSR)

// server-side
import { Provider } from 'react-redux';
import { apiSlice } from './services/api';

export async function getServerSideProps() {
  const { store } = initializeStore();
  
  // Загрузить данные на сервере
  await store.dispatch(
    apiSlice.endpoints.getPosts.initiate()
  );

  return {
    props: {
      initialReduxState: store.getState()
    }
  };
}

Преимущества RTK Query

  • Минимум boilerplate - не нужно писать reducers и actions
  • Автоматическое кеширование - умное управление кешем
  • Инвалидация кеша - через теги и зависимости
  • Реактивные хуки - простое использование в компонентах
  • DevTools интеграция - отличная отладка
  • TypeScript поддержка - полная типизация
  • SSR поддержка - готово к серверизации

RTK Query значительно снижает количество кода, необходимого для управления состоянием с сервера, и предотвращает множество распространённых ошибок.

Что в RTK Query облегчает разработку и создание запросов на сервер? | PrepBro