Как использовать Redux-Saga внутри класса - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть идея отделить sagas в соответствии с их ответственностью. Я создал базовый класс для обработчиков саги следующим образом.

 export class BaseSagaHandler {

  static isGenerator(fn) {
    return fn.endsWith('Gen');
  }

  forkAllSagaFunctions() {
    const sagaFuncs = [];
    const functions = Object.getOwnPropertyNames(this.constructor.prototype);
    functions.forEach(func => {
      if (BaseSagaHandler.isGenerator(String(func))) {
        sagaFuncs.push(fork([this, this[func]]));
      }
    });
    return sagaFuncs;
  }
}

Как видно из приведенного выше кода, я придерживаюсь соглашения, что нам нужно использовать все методы, которые заканчиваются на Gen как Sagas. Вот пример дочернего класса

export class LocaleSaga extends BaseSagaHandler {
  private localeService: ILocaleService;

  constructor(localeService: ILocaleService) {
    super();
    this.localeService = localeService;
  }

  public* setCurrentLocale(locale) {
    yield call([this, this.localeService.setCurrentLocale], locale);
    yield put(WITH_PAYLOAD(SUCCESS(ACTION_TYPES.SET_LOCALE), locale));
  }

  public* setLocaleSagaGen() {
    yield takeLatest(REQUEST(ACTION_TYPES.SET_LOCALE), this.setCurrentLocale);
  }
}

Этот класс создается следующим образом.

const diContainer = rootDiContainer;
const localeSagas = new LocaleSaga(diContainer.get<ILocaleService>(LOCALE_SERVICE_TYPE)).forkAllSagaFunctions();
export default function* rootSaga() {
  yield all([
    localeSagas
  ]);
}

Так что в основном мы слушаем конкретное действие, а затем вызываем функцию. Однако у меня есть проблема в этой строке.

yield takeLatest(REQUEST(ACTION_TYPES.SET_LOCALE), this.setCurrentLocale);

Насколько я передаю метод класса, когда он достигает setCurrentLocale, функция this равна null. Методы take* не поддерживают передачу контекста, как я мог видеть из исходного кода и документации, но функции fork,call позволяют передавать context вместе с функцией.

Есть ли способ передать ссылку на метод класса в контексте класса?

P.S. Пожалуйста, дайте мне знать, есть ли в моей идее подводные камни или нет смысла.

Спасибо.

UPDATE

Благодаря ответу Андрея Моисеева я решил объединить два метода в один с функцией анонимного генератора следующим образом.

public* setLocaleSagaGen() {
    const context = this;
    yield takeEvery(REQUEST(ACTION_TYPES.SET_LOCALE), function* (locale) {
      const result = yield call([context.localeService, context.localeService.setCurrentLocale], locale);
      yield put(WITH_PAYLOAD(SUCCESS(ACTION_TYPES.SET_LOCALE), result));
    });
  }

1 Ответ

0 голосов
/ 08 ноября 2018

В Javascript this зависит от того, откуда вы вызываете функцию с .

Проблема в том, что сага, которую вы передаете в takeLatest(), называется позже изнутри внутренних частей реду-саги. До тех пор, пока redux-saga не примет context, сохранит его, а позже вручную предоставит его как this (к сожалению, это не так), он не будет работать.

Я вижу два возможных варианта.

Использование стандарта takeLatest()

  • Сохранить this как context в области действия функции.
  • Передайте встроенную сагу takeLatest(). Эта сага является закрытием, которое имеет доступ к context.

код:

public* setLocaleSagaGen() {
  const context = this
  yield takeLatest(REQUEST(ACTION_TYPES.SET_LOCALE), function*() {
    yield call([context, context.setCurrentLocale])
  })
}

Использование пользовательских takeLatest()

В документах redux-saga есть пример реализации takeLatest(). Я изменил его, чтобы принять и передать контекст:

const takeLatestWithContext = (patternOrChannel, [context, saga], ...args) => fork(function*() {
  let lastTask
  while (true) {
    const action = yield take(patternOrChannel)
    if (lastTask) {
      yield cancel(lastTask) // cancel is no-op if the task has already terminated
    }
    lastTask = yield fork([context, saga], ...args.concat(action))
  }
})

Так что его можно использовать как:

  public* setLocaleSagaGen() {
    const context = this
    yield takeLatest(REQUEST(ACTION_TYPES.SET_LOCALE), [this.context, this.setCurrentLocale]);
  }

В этом случае сохранение this не является необходимым, поскольку takeLatest() аргументы вычисляются немедленно.

...