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

Зачем нужен Thunk?

1.6 Junior🔥 181 комментариев
#Soft Skills и рабочие процессы

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

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

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

Что такое Thunk и зачем он нужен

Thunk — это функция, которая откладывает вычисление или выполнение кода до момента, когда он действительно понадобится. В контексте Redux и асинхронного программирования на JavaScript, Thunk решает важную проблему: как отправить асинхронные действия в store.

Проблема, которую решает Thunk

Redux по умолчанию работает только с синхронными действиями (actions). Если нужно выполнить асинхронную операцию (HTTP запрос, чтение файла, таймер), то обычный action dispatch не поможет:

// Это не работает с Redux
const fetchUser = (id) => {
  const response = await fetch(`/api/users/${id}`);
  return {
    type: 'FETCH_USER_SUCCESS',
    payload: response.json(),
  };
};

dispatch(fetchUser(1)); // Ошибка: не можем await

Решение: Redux Thunk Middleware

Redux Thunk — это middleware, который позволяет dispatch-ить не просто actions, а функции (thunks). Функция-thunk получает dispatch и getState как аргументы и может выполнять асинхронный код:

import { createAsyncThunk } from '@reduxjs/toolkit';

// Создаём async thunk
const fetchUser = createAsyncThunk(
  'users/fetchUser',
  async (id, { rejectWithValue }) => {
    try {
      const response = await fetch(`/api/users/${id}`);
      if (!response.ok) throw new Error('Network error');
      return await response.json();
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

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

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const { data, loading, error } = useSelector(state => state.users);

  useEffect(() => {
    dispatch(fetchUser(userId));
  }, [userId, dispatch]);

  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error}</div>;
  return <div>{data.name}</div>;
}

Как работает Thunk

  1. Диспатч функции — вместо объекта-action отправляем функцию
  2. Middleware перехватывает — Redux Thunk перехватывает функцию
  3. Выполняет код — middleware вызывает функцию с dispatch и getState
  4. Отправляет actions — внутри функции можем вызвать dispatch несколько раз:
// Старый стиль (без Redux Toolkit)
const fetchUserThunk = (id) => async (dispatch, getState) => {
  dispatch({ type: 'FETCH_USER_START' });
  
  try {
    const response = await fetch(`/api/users/${id}`);
    const data = await response.json();
    
    dispatch({
      type: 'FETCH_USER_SUCCESS',
      payload: data,
    });
  } catch (error) {
    dispatch({
      type: 'FETCH_USER_ERROR',
      payload: error.message,
    });
  }
};

dispatch(fetchUserThunk(1)); // Работает!

Redux Toolkit (современный подход)

Redux Toolkit упрощает работу с thunks через createAsyncThunk:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

interface User {
  id: number;
  name: string;
  email: string;
}

interface UsersState {
  data: User | null;
  loading: boolean;
  error: string | null;
}

const fetchUser = createAsyncThunk<User, number>(
  'users/fetchUser',
  async (id, { rejectWithValue }) => {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      return rejectWithValue('Failed to fetch user');
    }
    return response.json();
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: { data: null, loading: false, error: null } as UsersState,
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload as string;
      });
  },
});

export default usersSlice.reducer;

Альтернативы Thunk

  1. Redux Saga — более мощная, для сложной асинхронной логики
  2. Redux Observable — для асинхронного управления потоками событий
  3. TanStack Query — специально для работы с серверными данными (рекомендуется вместо Redux для запросов)

Когда использовать Thunk

  • Асинхронные HTTP запросы (fetch, axios)
  • Работа с сетью и таймерами
  • Сложная асинхронная логика приложения
  • Когда нужен доступ к getState для проверки текущего состояния

Резюме

Thunk — это функция, которая откладывает вычисления и позволяет Redux работать с асинхронным кодом. Redux Toolkit предоставляет удобный API через createAsyncThunk, который автоматически генерирует pending/fulfilled/rejected actions. Для простых запросов к серверу рекомендуется использовать TanStack Query вместо Redux.