Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequence of actions with redux-observable

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?

like image 845
jeanpaul62 Avatar asked Jan 01 '26 17:01

jeanpaul62


1 Answers

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.

like image 127
seniorquico Avatar answered Jan 05 '26 21:01

seniorquico



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!