Безопасно ли вызывать sagaMiddleware.run несколько раз? - PullRequest
0 голосов
/ 14 сентября 2018

Я использую redux и redux-saga в приложении для управления состоянием и асинхронными действиями. Чтобы упростить мою жизнь, я написал класс, который по сути выступает в роли менеджера саги, с методом, который «регистрирует» сагу. Этот метод регистрации разветвляет новую сагу и объединяет ее со всеми другими зарегистрированными сагами, используя redux-saga/effects/all:

class SagasManager {
    public registerSaga = (saga: any) => {
        this._sagas.push(fork(saga));
        this._combined = all(this._sagas);
    }
}

Этот класс затем используется моим магазином для получения саги _combined, предположительно, после регистрации всех саг:

const store = Redux.createStore(
    reducer, 
    initialState, 
    compose(Redux.applyMiddleware(sagaMiddleware, otherMiddleware)),
);
sagaMiddleware.run(sagasManager.getSaga());

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

Я решил эту проблему, предоставив обратный вызов SagasManager:

class SagasManager {
    public registerSaga = (saga: any) => {
        this._sagas.push(fork(saga));
        this._combined = all(this._sagas);
        this.onSagaRegister();
    }
}

И тогда код магазина может использовать это как

sagasManager.onSagaRegister = () => sagaMiddleware.run(sagasManager.getSaga());

Кажется, это работает, но я не могу найти в документах, безопасно ли это. Я видел, что .run возвращает Task, в котором есть методы отмены и тому подобное, но так как моя проблема только в том неловком времени между созданием хранилища и отображением приложения, я бы этого не сделал вопрос.

Может кто-нибудь объяснить, безопасно ли это, а если нет, то каким будет лучшее решение?

1 Ответ

0 голосов
/ 15 сентября 2018

Это может зависеть от того, что вы подразумеваете под «безопасным».Что именно вы подразумеваете под этим в этом случае?

Во-первых, вот источник самого runSaga и где он используется промежуточным программным обеспечением саги.

Заглянув внутрь runSaga, я вижу:

export function runSaga(options, saga, ...args) {
  const iterator = saga(...args)

  // skip a bunch of code

  const env = {
    stdChannel: channel,
    dispatch: wrapSagaDispatch(dispatch),
    getState,
    sagaMonitor,
    logError,
    onError,
    finalizeRunEffect,
  }

  const task = proc(env, iterator, context, effectId, getMetaInfo(saga), null)

  if (sagaMonitor) {
    sagaMonitor.effectResolved(effectId, task)
  }

  return task
}

Из этого я получаю то, что ничего «разрушительного» не произойдет, когда вы позвоните runSaga(mySagaFunction).Однако, если вы вызываете runSaga() с одной и той же функцией саги несколько раз, кажется, что у вас, вероятно, будет несколько копий этой саги, что может привести к тому, что ваше приложение не захочет.

Вы можете попробовать поэкспериментировать с этим.Например, что произойдет, если у вас есть приложение счетчика, и вы делаете это?

function* doIncrement() {
    yield take("DO_INCREMENT");
    put({type : "INCREMENT"});
}

sagaMiddleware.runSaga(doIncrement);
sagaMiddleware.runSaga(doIncrement);

store.dispatch({type : "DO_INCREMENT"});

console.log(store.getState().counter);
// what's the value?

Я предполагаю, что счетчик будет равен 2, потому что обе копии doIncrement ответили бы.

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

Я действительно наткнулся на рецепт отмены саг во время горячей перезагрузки некоторое время назад, и включил версию этого в суть для моего собственного использования .Вы можете обратиться к этому за идеями.

...