React without Redux, or How I learnt to embrace RxJS
Tags: react, redux, rxjs, javascript
Published: 2019-06-19 17:30:50

---
title: React without Redux, or How I learnt to embrace RxJS
published: true
description: 
tags: react, redux, rxjs, javascript
---

Whenever I start a new Webapp, I pretty much have the exact same set of libraries that I go to. React and Redux are high on that list. 

However, a lot of the time I find that the Redux store is being used for things that are very specific to one particular area of the UI, and *not* for more global state. As such, recently I decided to try a different approach. Namely, the Context API and RxJS.

Now, I've barely even started, but it already feels like it has potential.

My first task here was authentication. It's an app that you *must* be logged in to be able to do anything, so this was quite important. And to help streamline things, I've gone for the approach of separating the Email Address entry from the Login / Register forms, so that the system can detect if you're already registered or not and show the correct form.

What this means is that I've got the following React component hierarchy:

* App
  * HomePage
    * LoginRegister
      * EmailEntry
      * Login
      * Register

The `EmailEntry` component displays a simple form that asks for an Email Address. When the user submits one, it triggers an action to look up the email in the server, and then causes the `LoginRegister` component to render either the `Login` or `Register` components as appropriate. In short, the state transitions are:

* undefined => EmailEntry
* PENDING => EmailEntry, but with the loading indication to show that it's working
* EXISTS => Login
* UNKNOWN => Register

So, this all went into Redux and it all worked. The `EmailEntry` component dispatched the `checkEmailAddress` action. This caused Redux Saga to trigger, which:

* Dispatches the `checkEmailAddress_STARTED` action
* Makes the API call
* Dispatches the `checkEmailAddress_SUCCESS` action with the payload of `true` or `false`
* Dispatches the `checkEmailAddress_FINISHED` action

Reducers are then set up for the `checkEmailAddress_STARTED` and `checkEmailAddress_SUCCESS` actions to update the store values for `emailValue` and `emailStatus` as appropriate. 

The `LoginRegister` component is then set to react to the `emailStatus` value and render as appropriate.

This is all very simple Redux. But it's also a lot of code. And almost all of this is very specific to this specific hierarchy of components. Nothing else in the application cares about the fact that we're checking an email address, what the email address is or what the status of the check is. And yet, it's in the global store for everything to see.

So, I re-wrote it. I ripped Redux out entirely and instead wrote the following:

* A simple module called `checkEmailService` that has a single method - `checkEmail`. This takes the email address and returns an `Observable` for the result.
* When the form on the `EmailEntry` form is submitted we then:
  * Update local state to show that the form is pending
  * Call the `checkEmail` method with the entered address
  * Subscribe to the returned `Observable`. When it resolves we call a callback provided from `LoginRegister` with the email address and the result of the API call
* When the `LoginRegister` callback is triggered we update local state with the provided email address and status of it
* The `LoginRegister` component then uses this local state to determine which component to render.

This means that:
* The pending flag is local only to the `EmailEntry` component
* The email address and status are local only to the `LoginRegister` component
* There is *no* global state at all

That already feels cleaner. We've got rid of any global state, which is a huge plus (We all know how bad global variables are. Why is global state any better?)

Sometimes though we do have values that are important to more of the application. For example, the Current User might be important, or the Authenticated Access Token. I've not yet implemented these, but I have two approaches for them in mind.

For the actual global values, I'm going to use a `Subject` - specifically a `BehaviorSubject` - instead of an `Observable`. The service calls can then update this as and when needed, and anything can subscribe to the current value. Access Token is one such value - it starts `undefined`, but on authenticating it will be given a value. Anything that needs the current value will then be able to get it from the `Subject` using `getValue`, or can subscribe to get notified whenever it changes.

For UI-centric concerns, I'm considering coupling this with the Context API and have a component in the appropriate part of the component tree act as the Provider and subscribe to the `Subject`. Whenever the `Subject` changes, this component updates it's local value and passes it into the Context API. Anything lower down that needs it can then access it from the Context API without needing to know about the API calls that generated it. This means that there's only a single subscriber to the `Subject` that needs to do the updates, and React handles the rest.

All of this seems to give me the majority of Redux functionality without any need for Redux itself.

The bit that's missing is orchestration. The fact that a single dispatched action can cause multiple bits of the store to react. This is also relatively simple to achieve by simply having service APIs that call other service APIs. For example, the act of authentication is:

* Send the Email and Password to the Server, and get back an Access Token and User ID
* Store the Access Token
* Store the User ID as the Current User ID
* Call the server to get the User Details for the Current User ID

Redux allows a lot of this to happen by different parts of the store reacting to the same actions. For example:

* `authenticate_SUCCESS` causes the Access Token Reducer to store the Access Token
* `authenticate_SUCCESS` causes the Current User Reducer to store the User ID
* `authenticate_SUCCESS` causes a Saga to dispatch the `getUser` action with the given User ID
* `getUser_SUCCESS` causes the User Details Reducer to store the user details

All chained off of a single action. That works, but it's difficult to trace through it in the code. Instead, I'm planning on having an `authenticationService` which:

* Calls the `accessTokenService` to get the Access Token
* Calls the `currentUserService` to store the User ID
* Calls the `getUserService` to get (and cache) the User Details

This gives very readable orchestration, and makes debugging and testing it very simple.

Will it work? I don't know yet.

Will it be better than Redux? I don't know yet.

But I fully intend to see how it goes.