Detecting state changes with Redux-Saga

11 Oct 2017

Cover image: Detecting state changes with Redux-Saga

Redux-Saga allows us to reactively respond to Redux actions swiftly:

function* someSaga() {
  yield take('ACTION_NAME');
  // will be executed after ACTION_NAME was dispatched

Sometimes, though, rather than relying on (or even caring for) the actions themselves, we want to observe the changes in some part of the Redux state.

Say we wanted to ensure we only run some actions:

  • if there is a current user, proceed;
  • if there is no current user, wait for it, and only then proceed;

Who the current user is, is stored somewhere in the state and can be changed by at least three actions: LOG_IN, SIGN_UP, and REHYDRATE_SESSION.

But to implement the requirement, simply listening for the actions is not enough. We'd also have to check the state — in case the user is already logged in:

function* someSaga() {
  if (!(yield select(getCurrentUser))) {
    yield take('LOG_IN', 'SIGN_UP', 'REHYDRATE_SESSION');
  // the user is logged in by now

Isn't it redundant to specify the exact actions if we are checking a part of the state anyway?

Instead, would this be a better API?

function* someSaga() {
  yield call(waitFor, state => getCurrentUser(state) != null);
  // the user is logged in by now

Here's how we could implement this waitFor function:

  1. Run the selector first. If it returns true, exit the function.
  2. Otherwise take every action and run the selector. Run it until it returns true. Then exit the function.

In code, it would be:

function* waitFor(selector) {
  if (yield select(selector)) return; // (1)

  while (true) {
    yield take('*'); // (1a)
    if (yield select(selector)) return; // (1b)

(Passing * as the action type in 1b means take will wait for any action.)

And now we have a simple but powerful utility for detecting state changes.

Think your friends would dig this article, too?

If you need a mobile app built for your business or your idea, there's a chance I could help you with that.
Leave your email here and I will get back to you shortly.