3

Here's the flow I want to implement: the list of all users is publicly available on my backend API, so I wish to see if the user already exists, if no, create a new user.

I'm using Redux, and I'm managing side effects with redux-observable. Here's the flow, with the relevant actions I'm sending.

  • Button click dispatches the action FIND_OR_CREATE_USER. Side effect should handle the next steps.
  • Find user in the API backend - dispatch GET_USER_BY_ID_REQUEST with a userId param, side-effect will do the HTTP call.
  • Receive user - dispatch GET_USER_BY_ID_SUCCESS, the response is stored in the state.
  • If this response is empty, then create a user with CREATE_USER_REQUEST.

Now the only thing is that this GET_USER_BY_ID_SUCCESS is also used somewhere else in the app (e.g. to get a user profile). So I cannot listen to GET_USER_BY_ID_SUCCESS and create a user right after it if the response is empty. Only create a user if we have a GET_USER_BY_ID_SUCCESS which follows a FIND_OR_CREATE_USER.

Here's how I would do it with redux-sagas:

function* findOrCreateUserSaga() {
  yield put({ type: 'GET_USER_BY_ID_REQUEST', payload: userId });
  yield take('GET_USER_BY_ID_SUCCESS');
  const response = yield select((state) => state.user);
  if (!response) {
    yield put({ type: 'CREATE_USER' }, payload: someObject );
  }
}

...

yield takeLatest('FIND_OR_CREATE_USER', findOrCreateUserSaga)

How can I implement the same flow in a reactive way, with redux-observable?

jeanpaul62
  • 9,451
  • 13
  • 54
  • 94

1 Answers1

2

You can create new subscriptions to the action$ stream at any point in time in an epic. This allows you to dynamically start/stop listening for specific actions in response to other actions, workflows, etc..

Here's the example saga translated to RxJS 6 (using pipes) and redux-observable 1+ (using the state$ stream). It uses the merge function to create an observable that both emits the "GET_USER_BY_ID_REQUEST" and listens for "GET_USER_BY_ID_SUCCESS". Additionally, the first operator used within the inner action$ stream pipe causes it to complete when your matching action occurs.

const epic = (action$, state$) => action$.pipe(
  ofType('FIND_OR_CREATE_USER'),
  switchMap(action =>
    merge(
      of({ type: 'GET_USER_BY_ID_REQUEST', payload: userId }),
      action$.pipe(
        ofType('GET_USER_BY_ID_SUCCESS'),
        first(),
        withLatestFrom(state$),
        mergeMap(([action, state]) =>
          state.user
            ? empty()
            : of({ type: 'CREATE_USER', payload: someObject })
        ),
      ),
    )
  ),
)

The example saga didn't specify how the userId and someObject variables are created. Note that the same is true of the above example epic. However, it should be easy enough to fill in these gaps based on your specific use case.

seniorquico
  • 1,419
  • 1
  • 12
  • 22