Является ли идиоматичным использование redux-saga для вызова рабочей саги непосредственно из компонента React? - PullRequest
1 голос
/ 12 июля 2019

Я использую redux-saga, потратив некоторое время на основные концепции генераторов, генераторов с обещаниями и самой Реду-саги. Ниже я хочу понять, что идиоматично и рекомендовано, а что нет.

В одном файле я определяю свою корневую сагу, сагу-наблюдателя и одну рабочую сагу (fetchSupplierOrders).

import {
    fetchSupplierOrders,
} from './supplierOrders';
import { takeLatest} from 'redux-saga/effects';

function* watchSupplierOrdersSagas() {
    yield takeLatest('REQUEST_FETCH_SUPPLIER_ORDERS', fetchSupplierOrders);
}

export default function* rootSaga() {
    yield all([watchSupplierOrdersSagas()]);
}

Вот рабочая сага:

export function* fetchSupplierOrders() {
    try {
        const supplierOrders = yield call(getSupplierOrders);  // API call is in getSupplierOrders
        // normally here I would use redux-saga put to hit my redux reducers
        yield supplierOrders.map(({ id }) => id)
    } catch (error) {
        yield put({ type: 'RECEIVE_ERROR_FETCH_SUPPLIER_ORDERS', error: error.message });
    }
}

У меня есть компонент React, который, когда я нажимаю кнопку, выполняет рабочую сагу. То, что я пытаюсь сделать здесь, - это вообще не проходить через сагу «Наблюдатель за редуксой сагой». Я просто сам выполню функцию генератора в компоненте и переберу ее. Обычно я проходил через сагу-наблюдатель и вызывал рабочую сагу, которая генерировала бы побочные эффекты, изменяя состояние избыточности.

Однако, что если я хочу сделать сетевой запрос, но я не хочу сохранять результат в избыточном, а в состоянии локального компонента? Я хочу, чтобы компонент каким-то образом получил результаты из рабочей саги напрямую.

Вот обработчик щелчка для кнопки в компоненте React:

    const handleFetchSuppliers = event => {
        const it = fetchSupplierOrders({ payload: event.target.value });
        const res1 = await it.next().value;
        console.log('res1', res1);
        const res2 = it.next(res1);
        console.log('res2', res2);

Этот код не будет работать, потому что в рабочей саге я использую функцию redux-saga call. Если я уберу использование call и вызову getSupplierOrders (асинхронную функцию) напрямую, то ожидание сработает, и все правильные значения будут сохранены в console.logged.

Обычно это делается (выполнение рабочей саги из компонента, чтобы получить результаты запроса API)? Но если я делаю это таким образом, то теряю преимущество использования call (разве это не полезно, потому что его проще тестировать?)

До redux-saga Я бы просто отправил Thunk, используя redux-thunk, который в основном использует async/await на всем протяжении.

Смешивают ли люди использование redux-thunk и redux-saga? Это осуждается?

1 Ответ

2 голосов
/ 12 июля 2019

Однако что, если я хочу сделать сетевой запрос, но я не хочу сохранять результат в избыточном, а в состоянии локального компонента?

Если редукс не задействован, тогда редукс-сага не является подходящим инструментом для использования. Просто используйте обычный подход реакции: сделайте запрос API (часто в componentDidMount), затем дождитесь завершения этого обещания (с помощью .then или await), затем вызовите setState.

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

Например:

// helper
async function fetchStuff() {
  const response = await fetch('some Url');
  if (!response.ok) {
    throw response.status;
  }
  const data = await response.json();
  return data.map(({ id }) => id);
}

// In a saga...
function* watchFetch() {
  yield takeLatest('doFetch', fetchStuffSaga);
}

function* fetchStuffSaga() {
  try { 
    const data = yield call(fetchStuff);
    yield put({ type: 'success', payload: data });
  } catch (err) {
    yield put({ type: 'error', payload: err });
  } 
}

// In a component that dispatches an action:
componentDidMount() {
  this.props.dispatch({ type: 'doFetch' });
}


// In a component that doesn't want to dispatch an action:
async componentDidMount() {
  try {
    const data = await fetchStuff();
    this.setState({ data });
  } catch (err) {
    this.setState({ error: err });
  }
}

Этот код не будет работать, потому что в рабочей саге я использую функцию вызова redux-saga. Если я уберу использование call и вызову getSupplierOrders (асинхронную функцию) напрямую, то await сработает, и все правильные значения будут console.logged.

Саги не предназначены для ручной итерации. Если вы попытаетесь выполнить итерацию саги вручную, вам нужно либо иметь специальные знания о том, что именно даст сага в каком порядке, либо вам, по сути, нужно заново внедрить redux-saga самостоятельно. Первый хрупок и тесно связан, последний - пустая трата усилий.

Обычно это делается (выполнение рабочей саги из компонента для получения результатов запроса API)?

* * Номер тысяча двадцать-один * * тысяча двадцать две

Смешивают ли люди использование redux-thunk и redux-saga? Это осуждается?

Они оба пытаются обрабатывать одни и те же вещи (асинхронные действия). Ваша кодовая база будет проще, если вы будете использовать только один подход, а затем пытаться смешивать и сочетать оба.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...