JavaRush /Курсы /Модуль 3: React /Разбор кейсов применения Thunk и Saga

Разбор кейсов применения Thunk и Saga

Модуль 3: React
7 уровень , 9 лекция
Открыта

Thunk VS Saga: краткое сравнение с точки зрения реальной жизни

Подводя итоги, давайте погрузимся в сравнение и разбор практических кейсов применения Thunk и Saga. Постараемся понять, когда, зачем и почему использовать ту или иную библиотеку.

Представьте, что у вас есть два курьера, которым вы поручаете приносить вам пиццу из разных мест.

  • Thunk — это курьер с велосипедом: быстрый, простой и доступный. Он берет задание, едет в пиццерию, забирает пиццу и возвращает её вам. Но... если вы попросите его доставить пиццу и ещё параллельно купить молоко и газировку, ему будет сложновато. Thunk подходит для простых или средних задач, где не требуется слишком много логики.

  • Saga — это профессиональный шофер с машиной. Он умеет не только забирать пиццу, но и планировать маршрут, следить за временем, а если по дороге что-то пойдет не так (например, магазин закроется), он попробует альтернативные варианты. Saga подходит для сложных задач и более изощренных цепочек действий.

Теперь, зная, кто ваш идеальный курьер, давайте углубимся в реализацию и разберём, как выбрать подходящий инструмент.

Кейс #1: простое приложение с минимальной логикой

Ваше приложение — это, скажем, список задач (To-Do). Пользователи могут добавлять, удалять и изменять задачи. Всё это хранится в удалённой базе через API.

Подход: используем Redux Thunk!

Почему Thunk? Потому что реализация проста: вам нужно выполнить простой HTTP-запрос и обновить состояние. Вот пример:

// actions/todoActions.ts
import { Dispatch } from 'redux';
import { Todo } from '../types';

export const fetchTodos = () => {
  return async (dispatch: Dispatch) => {
    dispatch({ type: 'FETCH_TODOS_REQUEST' });

    try {
      const response = await fetch('/api/todos');
      const data: Todo[] = await response.json();
      dispatch({ type: 'FETCH_TODOS_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_TODOS_FAILURE', error: String(error) });
    }
  };
};

Типизация? Легко. Просто используем Dispatch из Redux. Для такого простого сценария Thunk прекрасно подходит: компонент отправляет запрос, запрос возвращает данные, всё обновляется. Быстро, эффективно и удобно.

Проблемы

Если логика запросов усложнится (например, появится необходимость обработать несколько API-запросов, чей результат влияет друг на друга), Thunk может стать запутанным и сложным для отладки.

Кейс #2: реакция на множество действий

Представьте приложение, которое получает данные о погоде. Вы должны:

  1. Запрашивать данные каждые 10 минут.
  2. Обработать ошибку, если API недоступно.
  3. Уведомлять пользователя о новой погоде.

Подход: это работа для Redux Saga

Почему Saga? Потому что она позволяет легко управлять сложными асинхронными процессами через генераторы. Вот как это можно сделать:

// sagas/weatherSaga.ts
import { call, put, takeEvery, delay } from 'redux-saga/effects';
import { fetchWeatherApi } from '../api/weatherApi';
import { WEATHER_REQUEST, WEATHER_SUCCESS, WEATHER_FAILURE } from '../actions/types';

function* fetchWeather(): Generator {
  while (true) {
    try {
      const data = yield call(fetchWeatherApi);
      yield put({ type: WEATHER_SUCCESS, payload: data });
    } catch (error) {
      yield put({ type: WEATHER_FAILURE, error });
    }
    yield delay(600000); // Повторить через 10 минут
  }
}

export function* watchWeatherSaga(): Generator {
  yield takeEvery(WEATHER_REQUEST, fetchWeather);
}

Почему это круто

Saga обрабатывает цикл без каких-либо лишних вмешательств. Вся логика гибко разделена: чтение данных с API, диспетчеризация действий, ошибки и повторные запросы. Попробуйте написать это же на Thunk? Получится большая плоскость вложенных функций и условий.

Кейс #3: взаимодействие между действиями

Предположим, вы создаёте приложение для e-commerce. После успешного логина пользователя вы хотите сразу загрузить его корзину и историю заказов. Но если логин не удался, запросы делать не нужно.

Подход: Redux Saga идеально подходит для этого.

Saga обеспечивает прямое управление потоком выполнения. Сначала проверяем успешность логина, если всё ок, начинаем загрузку корзины и истории. Вот пример:

// sagas/authSaga.ts
import { call, put, takeEvery, all } from 'redux-saga/effects';
import { loginApi, fetchCartApi, fetchOrdersApi } from '../api';
import { LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, FETCH_CART, FETCH_ORDERS } from '../actions/types';

function* login(action: any): Generator {
  try {
    const user = yield call(loginApi, action.payload);
    yield put({ type: LOGIN_SUCCESS, payload: user });

    // После успешного логина параллельно загружаем корзину и заказы
    yield all([
      put({ type: FETCH_CART }),
      put({ type: FETCH_ORDERS })
    ]);
  } catch (error) {
    yield put({ type: LOGIN_FAILURE, error });
  }
}

export function* watchAuthSaga(): Generator {
  yield takeEvery(LOGIN_REQUEST, login);
}

С помощью call и all можно управлять несколькими процессами, используя минималистичный и понятный код.

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

  • Redux Thunk хорош для:

    • Простых запросов (CRUD-операции).
    • Маленьких приложений.
    • Если вы хотите начать быстро и не ломать голову над новым инструментом.
  • Redux Saga подходит для:

    • Сложных потоков данных.
    • Последовательных или параллельных операций.
    • Сложной обработки ошибок или повторных попыток.
    • Массивных приложений, где вам потребуется легко расширять и поддерживать асинхронные процессы.

Типичные ошибки и их решения

  1. Вложенные функции с Thunk. Если вы замечаете, что ваши Thunk-функции начинают напоминать спагетти, это верный признак того, что пора переключиться на Saga.

  2. Слишком сложные саги. Иногда разработчики пытаются написать мега-сагу, которая управляет всем сразу. Это плохо. Разделяйте логику: одна сага — одна задача. Избыточная сложность приводит к трудностям в отладке.

  3. Отсутствие типизации. В любом подходе, будь то Thunk или Saga, обязательно типизируйте состояния и действия. Это не только поможет избежать ошибок, но и существенно упростит понимание кода.

Итак, выбор между Thunk и Saga — это как выбор между велосипедом и машиной. Велосипед легче, дешевле и быстрее для коротких поездок, но если вам предстоит долгий и сложный путь (с препятствиями), лучше пересесть в машину. Выбор за вами и зависит от контекста вашего проекта и задач.

1
Задача
Модуль 3: React, 7 уровень, 9 лекция
Недоступна
Периодическое обновление данных с помощью Redux Saga
Периодическое обновление данных с помощью Redux Saga
1
Задача
Модуль 3: React, 7 уровень, 9 лекция
Недоступна
Взаимозависимые действия с использованием Redux Saga
Взаимозависимые действия с использованием Redux Saga
3
Опрос
Redux Saga, 7 уровень, 9 лекция
Недоступен
Redux Saga
Redux Saga и его преимущества перед Thunk
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ