Действие по отмененной наблюдаемой

Здравствуйте и заранее спасибо :)

Основная идея

Я хочу запускать определенные эпики action$ из redux-observable в зависимости от маршрута и отменять их при изменении маршрута. Также я хочу провести некоторую очистку, когда эпики отменяются. Я сделал это. Но:

Проблема

Я использую state.dispatch(actions.signInError({})) для очистки (это устарело) и не знаю, как сделать это по-другому. Мой код ниже, проблема в самом конце.

Изменить эпики при изменении маршрута

/**
 * Launch route specific actions if such exist and cancel previous one
 *
 * @param {Function} action$ - redux-observable action$
 * @param {Object} state - redux state
 */
const addEpicsForRoute = (action$, state) => action$
    .ofType(LOCATION_CHANGE) // action from route
    .switchMap(
        ( action ) => {
            // get epics for route
            const epicsForRoute = routeEpics[ action.payload.pathname ];

            if ( epicsForRoute ) {
                return merge(
                    ...epicsForRoute.map(observableCreator => observableCreator(action$, state))
                );
            } else {
                return empty(); // no specific epics
            }
        }
    );

Какая-то конкретная эпопея для какого-то маршрута

/**
 * Handle xhr request-response/error logic of sign in user
 *
 * @param {Function} action$
 * @param {Object} state
 */
export const signIn = ( action$, state ) => {
    return action$
        .ofType(types.SIGN_IN_REQUEST)
        .mergeMap(( { params, } ) => (
            Observable.create(observer => {
                services
                    .signInRequest( // it is ajax observable
                        mappers.mapSignInRequest(params)
                    )
                    .map(response => actions.signInJWTSuccess( // dispatch success
                        mappers.mapUser(response)
                    ))
                    .catch(error => of(actions.signInError( // dispatch error
                        mappers.mapSignInError(error)
                    )))
                    .subscribe(( value ) => { // pass action to redux-store
                        observer.next(value);
                    });

                return () => {
                    // cleanup logic. HERE IS A PROBLEM 
                    // calling store.dispatch() directly in your Epics is deprecated and will be removed.
                    // what should I use instead?
                    state.dispatch(actions.signInError({}));
                };
            })

        ));
};

Также я новичок в rxjs, и если у вас есть совет, как я могу улучшить или сделать код красивее, я более чем заинтересован!


person Arseniy-II    schedule 29.03.2018    source источник


Ответы (2)


Я предлагаю просмотреть поток кода, чтобы лучше использовать возможности операторов Observables.

Идея может заключаться в том, чтобы двигаться вдоль этих линий

export const signIn = ( action$, state ) => {
    return action$
        .ofType(types.SIGN_IN_REQUEST)
        .switchMap(( { params, } ) => (services.signInRequest( // it is ajax observable
                        mappers.mapSignInRequest(params)
                    ))
        .map(response => actions.signInJWTSuccess( // dispatch success
                        mappers.mapUser(response)
                    ))
        .catch(error => of(actions.signInError( // dispatch error
                        mappers.mapSignInError(error)
                    )))
};

Таким образом, вы создали функцию signIn, которая возвращает Observable, который выдает результат ajax-вызова signIn.

Затем я бы создал еще одну часть логики, чтобы подписаться на такой Observable, возвращаемый signIn, и решить, что делать, например.

const subscription = signIn(action, state)
.subscribe(
  value => {// do what needs to be done with the result of the signIn call},
  err => {// insert here the logic to handle error conditions},
  () => {// do here what needs to be done when the Observable completes
         // consider that ajax calls complete after the first emit, therefore
         // you can put this part of logic also within the first callback, the one passed as the first parameter to subscribe() method
        }
)

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

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

person Picci    schedule 29.03.2018

Я думаю, что хороший вопрос заключается в том, правильно ли это ожидание. Когда вы используете switchMap, вы отписываетесь от внутреннего Observable, что означает, что вы больше не хотите получать его выбросы. Так есть ли смысл в том, что он выдаст еще одно действие, когда вы отмените подписку?

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

const action$ = new Subject();
const cleanup$ = new Subject();

action$
  .pipe(
    switchMap(() => new Observable(observer => {
      // whatever goes here
      observer.next('Observable created');

      return () => {
        cleanup$.next(/* create proper action here */ 'cleanup');
      };
    })),
    merge(cleanup$),
  )
  .subscribe(console.log);

action$.next(1);
action$.next(2);

Я не использую настоящие redux-observable действия, но надеюсь, вы поняли суть.

Живая демонстрация (открытая консоль): https://stackblitz.com/edit/rxjs5-9iogn1?file=index.ts

person martin    schedule 31.03.2018