If you haven’t heard about reactive programming yet, I invite
you to pause and get acquainted.
This gist is a great place to get started. Reactive programming allows
your applications to react to events and data streams in real
time. This means you can skip life cycle methods like
componentDidUpdate when checking for changes in
your application state. If you are already familiar with
reactive programming, then hopefully this post can give you more
insights on how it can dovetail seamlessly with React using the
If you would like to skip to the end, the codesandbox is here
While learning about reactive programming in the context of React, I found a lot of great material, but not many tutorials outside of simple data fetching and counting. Here we will build a set of three lists. The first list contains users, while the other two containers list their likes and dislikes.
Clicking on a user should then load their likes and dislikes list and change the URL to denote which user is currently selected. If no user is specified in the the URL, then the first user on the list should be chosen automatically. Likes and dislikes should also be able to be added, deleted, and edited.
The main tools we will be using are recompose and RXJS. Both are utility libraries that help you harness the power of reactive programming. I would suggest looking through the docs of both libraries to get acquainted with their usage, but I will also explain how to use them in the context of building a UI in React.
UI design is supposed to feel snappy and reactive, and reactive programming allows us to change UI elements according to the flow of our data. Unlike in redux, we don’t have to update our data store, wait for the update to propagate, check to see if the update is finished, and finally update our UI to reflect the changes. With reactive programming, we can push changes to data streams and our components instantly react to these changes.
As our applications grow in size, so can the state management. The result is a large state object which needs to be traversed and updated even when you are only using a small part of it for the current page or view. In many cases, this data can be kept locally and can be ephemeral, no longer taking up space when the user navigates away from the page.
The first thing we need to do in this project is to set up
recompose to work with the observable configuration used by RXJS.
This is done by importing the function
setObservableConfig from recompose, and passing it
from utility from rxjs.
Now that we have recompose configured to work with RXJS, we can
start making prop streams for our component to consume. Let’s
first start with a
load stream which will fetch our
users from the backend and pass the response — as well as any
other props received — to the next stream.
Let’s break this down line by line.
First, we are importing a few operators from rxjs, so let’s
discuss what they do and why we need them.
map allow us to map values
to other functions and components.
switchMap takes an
observable and flattens it, allowing us to pass just its values to
the next function and not the observable itself.
The following operators,
startWith, help us with
debugging and allows us to give the user information about the
state of the application.
tap takes and observable,
performs a side effect, and returns a copy of the original
tap to debug observables
tap, we can log out the response we receive
from the backend without having to worry about changing anything
in our stream. Using the
catchError operators help us communicate the status
of the application to users by giving us the ability to do
conditional rendering based on it’s current state. We will go into
the implementation of this later.
Now that we understand what the operators we are using are able to
do, let’s step through the load stream logic. You will notice that
load itself is the result of the
mapPropsStream function taken from
recompose this takes as an argument a function which
receives a stream of props (streams are denoted by the suffix
$ for clarity, but that does not have any syntactic
meaning) which can then be piped through our logical operators.
Here is where
switchMap becomes very useful. Since
React can’t render anything with an Observable object, we need to
flatten our props stream so that we get only the values of that
stream. Those values are the actual props we need to render our
component. So we flatten the stream and map it to our function
which fetches the users from the backend. You can use
props here to pass any additional arguments to your
function that fetches the data, but since we’re faking it here, we
don’t need to pass anything. Finally, our request returns a
users array which we then map to an object which
passes along the
signals a successful request by setting
You may have noticed that we use
tap here as well to
call the function
setUserList, which is contained in
the props passed to the load stream. This, combined with the
ternary check of
isEmpty(props.userList will help us
prevent unnecessarily re-fetching the data. If we have the data,
we just want to return it without any manipulation.
Using streams to handle DOM events
Let’s create a second props stream which handles choosing a user from the list and setting them as a selected user:
Much like in our
load stream, we want to create a
stream of props using recompose’s
mapPropsStream function. However, instead of directly
switchMapping the props, we want to create two variables — a
stream and a handler — using another utility function from
createEventHandler will, as the name implies, create
a handler and a corresponding stream which you can then assign to
React events such as
etc. The values from that event will then be passed into the
stream which you can then pipe, map, and whatever else you need.
In our case, we want to flatten both the incoming props stream and
the stream from the event and produce a single object.
Taking our props, we want to check to see if there is a user
specified in the URL. In this project,
react-router-dom is being used so we find the user
props.match.params.user. But where is the
Route to pass the
match prop to this
stream?!? Don’t worry, we will get there, just know that for now,
selectUser will be passed the props from
Route component. Now that we have our
user from the URL, we want to start our stream with either that
user, or the first user in our
userList. If we don’t
userList yet, we just want to start with
null. Then we simply pipe the
selectedUser through the stream and return an object
which contains all props passed to
selectUser as well
In order to use the two prop stream we just created, we need to make a component which will take the props from both streams and render our list element
The component itself is quite simple, it just takes the props,
checks to see if the right user is in the URL, and if not, pushes
the correct user there. Next, it takes our
maps through it and returns a list item for each user in the list
and adds some conditional styling if the user is currently
selected. If the request is still ongoing, then it displays a
loading component. Notice that we pass the
userSelect function to our list items to use as an
onClick handler. The user that is passed to
userSelect is then pushed to the
In order for this component to have access to all these props,
it’s time for some functional programming beauty. Using the
compose function from recompose, we create a new
element which streams all the props from both
selectUser, and react-router to the
IndexPage element. It is here we will also create the
setUserlist and the
userList prop we saw in our
withHandlers allow us to keep persistent data and
manipulate that data inside our streams.
withState accepts three arguments, the name of the
item in state, the name of the function used to update the state,
and the inital state.
withHandlers receive the
function specified in
withState and can accept as
many handlers as you need. In our case, we just need one handler
which will take the
users array from our fetch
function and set the
userList state property to that
array. We also pass the
withRouter function so that
our component and event streams have access to router props such
Time to put the first steps together
Now that we have our list of users, it is time to create a
component which will display their lists of likes and dislikes.
Here, we can take advantage of more recompose helper functions,
getContext. These will
prevent us from being trapped in passed-down props hell. Since we
will be creating more handlers to update the list of users, there
will be many functions which we will want to pass down to our
components that display the likes and dislikes which could result
in something like this:
All of these props would then have to be continually passed down
until they reach the components that actually consume them. That
is crazy! By using
getContext, we can pass down the props we need and
consume them in any child of our
So, let’s create our handlers:
Here, are a few basic functions that will update our state after
making a request to a fictitious endpoint. We can now pass these
handlers and our selected user down to child components using
withContext takes two arguments, the first being the
childContextTypes, and the second being a function
which returns the props to be passed as context. In our case, we
want to pass the selected user and our state handler functions
down as context. We get these props by placing our
withContext function as an argument of our
compose function like this:
We are now able to consume these props as context in any child component!
Using context in child components
Let’s use the context we just created to display users’ likes and dislikes and add a simple interface to add and remove data.
The above component simply takes the user and their likes from
context and displays them in a list. This component also allows
users to delete a like by clicking on an icon which calls our
deleteUser function we defined earlier. Let’s add a
modal that will allow users to add an item to the list by clicking
It’s time to create more DOM event driven streams. The first stream we want to create will be one to open and close the form modal:
Handling a toggle event is quite simple, we specify whether we
want the toggle to be on or off, and then use
a function that applies an accumulator over the stream ( similar
to Array.reduce ) — to toggle the state. We then return all passed
props as well as the current state of the modal and the handler
Dealing with text input is a bit trickier:
In the above snippet, we take the stream produced by our event
handler ( in this case typing in a text input ) and we create a
new stream composed of only the value of
event.target. It is also important to note that we
startWith here or else this stream will not
be subscribed to and our component which uses this stream will not
To access these functions in our List component, we must compose them together like so:
By using composable streams, we are able to create a nice modal form which takes text input and adds it to the list of user’s likes. Here is it’s parent component:
And the modal itself:
Doing the same for the user’s dislikes gives us two lists that can be modified by the user.
Observable streams are a great way of managing data flows in
components. While some of the things outlined here might be a
bit overkill, I wanted to illustrate that composable streams
work well in many use cases. Breaking up logic into streams
can help create separate and composable pieces of logic that
can stem from complicated UIs. Components that subscribe to
changes in the streams are able to react to one source of
truth rather than having to check for side-effects in
lifecycle methods such as
think that they are a great way to remove some of the
anti-patterns we see in React components and to avoid the
performance costs of a large Redux store.