React Hooks + RxJS Facades

Use React Hooks with RxJS + State Management to create push-based architectures and radically clean-up your view components.

Early this year, I published a blog article on Push-based Architectures for Angular applications. In that earlier post, I provided code solutions for Angular developers. And I recorded a YouTube video of a panel discussion and presentation of those concepts in Push-based Architectures with RxJS.

Now with React v16.8 and Hooks, we can build equally elegant, performant push-based solutions with React. In this post, I provide similar mind-blowing ideas and code solutions for React developers!

Note: this is not a tutorial on React, React Hooks, RxJS, nor state management. This is an advanced blog on how to use those concepts to quickly build amazing architectures in React using TypeScript.

Push-based Architectures

With RxJS and Observable streams, developers can implement architectures that PUSH data changes to all interested subscribers.

Views simply subscribe to the desired data streams. When the ‘remote’ data changes — for any reason — that data will be auto-pushed through the stream to any interested view components. View components will re-render ONLY when specific data changes are pushed.

This approach is a fundamental, HUGE mental paradigm-shift from traditional pull-based or polling architectures.

What are the benefits of designing and using Push-based Architectures? With push-based services, developers can create applications using Passive View components. Passive view components have the following characteristics:

  1. Auto-render when data arrives via a push-stream.
  2. Delegate user interactions to a business layers.
  3. Focus on rendering, layouts, animations, and interactions.
  4. Have no knowledge of data sources, loading, processes, or logic.
  5. Do not contain business logic.
  6. Require no unit testing… only UX testing.

Consider the following React application:

Let’s dive into code and explore three (3) approaches to building this React application.

(1): React.Component classes with Data-Services
(2): React.Component classes with Akita Redux + Data-Services
(3): React Functional components with Custom Hooks + Akita Redux

Note: As each approach/solution is presented, you may notice that the view components will progressively evolve to a more simple and clean implementation!

Solution #1: Component Classes with Services

Here is the classic React architecture pattern where developers often mix together view logic and business logic. For testing, maintenance, and stability reasons, this approach is not ideal.

Here are the important code parts:

Our UsersService class manages the complexity of data loads, searching, and user selection. With the usersService singleton instance, the UsersPage view component uses the component lifecycle method componentDidMount to load the user list.

Note how the event handlers setActive and updateUser employ the UsersService business logic and then use this.setState() to trigger component rendering with the latest data values.

The imported singleton vm is a view-model (aka facade). Facades hide the complexities of the business and data-access layers. Facades — when used exclusively at the view layer [to provide specific properties and methods ] — are often called View Models.

Summary

This is the typical developer approach. It is also Pull-based Approach because the view pulls the data from the service. Pull-based solutions can create “stale data” and have serious flaws!

Swyx briefly discusses this topic in his blog “Deep-Dive: How do Hooks really work?”… see the section on “Stale Closures”.

Why?

  • What if another component changes the active user? How does the current UsersPages view component get notification regarding user changes and render with the new, active user?
  • What about the subscription to users$. When the component is unmounted, we will have a memory leak.
  • Our business logic is mixed inside the view component. View component logic must first call usersService methods and before calling setState().

Solution #2: Component with State Management

This version uses Redux + RxJS to manage state with RxJS observable streams: Akita State Management.

In this code below, Observables are used to stream the current users and active to the view component; and this.setState() is used to trigger rendering with the latest data.

Before exploring the demo, I suggest developers read @NetanelBasal’s excellent article: Managing State in React with Akita.

Under the hood, the UsersService is using a EntityState<User> instance to manage a collection of User records and a QueryEntity<UsersState, User> instance to quickly, easily build subscribable RxJS streams to internal data (aka state).

For Angular NgRx developers the semantics are very, very similar to @ngrx/entity.

Summary

Whenever the state changes, the current state values are pushed to subscribing view handlers and triggers view updates.

This is a great Push-based Approach!

This solution still manifests, however, a mixture of business logic (state management tooling) within the React.Component view code. We can improve upon this solution using the latest React techniques: React Hooks.

Solution #3: Functional Components with Hooks

Let’s use React Hooks, custom hooks, and facade (aka view-model) patterns to hide all the complexity from the UsersPage view layer.

Below, the custom hook useUsersFacade() will internally manage all issues regarding RxJS, Facades, and Akita. Note how the view uses the custom hook how the hook internally uses the standard React Hooks useState() and useEffect():

This is an elegant, push-based solution that cleanly partitions the business logic from the view layers.

The custom hook also manage RxJS subscriptions to auto-unsubscribe when the component is discarded (aka unmounted).

Summary

For our React application, you can see [below] the evolution of the UsersPages view component through each of the labs/solutions. As we re-architect each lab, the UsersPages view component becomes more clean, terse, and most maintainable.

Solution #3 is the obvious winner!

Using React Custom Hooks + RxJS Facades + State Management is an amazing approach to React applications. With this approach, your business code has now been moved into the business layer(s) and is easily tested.

  • Use Jest to test your business layers, facades, data services, and more
  • Use Cypress to test your UX workflows and UI stylings + layouts.

I cannot encourage developers strongly enough to use push-based architectures for their React or Angular applications.

Solutions architect expert in NextJS, React, NextJS and Angular solutions. Formerly a Principal Architect @ Nrwl.io, Team Lead for Angular, Google.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store