React Hooks + RxJS Facades

Thomas Burleson

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!

Super-clean, reactive React components

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.

Click to explore Demo + Source

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.

Click to explore Demo + Source

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).

Click to explore Demo + Source

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.

Click to zoom

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.

Thomas Burleson

Written by

Solution Architect with passion for React, Angular, UX, and great software products. Formerly Google Team Lead for AngularJS Material, @angular/flex-layout, …

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade