Thomas Burleson PRO
FE Architect, Technical Lead, and Engineering Coach. Delivering web solutions using React, NextJS, Angular, and TypeScript.
All Rights Reserved
Copyright 2021
Mindspace, LLC
@ThomasBurleson
4-Part Series
Reactive Programming
Part 1 of 5
What is
"A program is said to be reactive when an input change leads to a corresponding change in output without any need to update the output change manually."
Reactive Programming
Reactive features for UI & Services
With raw Javascript or off-the-shelf React or Angular, we can easily make solutions that react to user interactions and component state changes....
React
Angular
JavaScript / TypeScript
Reactive Programming
Reactive Systems
Reactive Programming
React / Angular UI
+
Web protocols use:
Request - Response
Client
Server
HTTP Request
HTTP Response
2 Protocols:
aka Pull-based
aka Push-based
Protocols:
Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.
Active
Active
Protocols:
Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.
Active
Active
Protocols:
Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.
Active
Active
Protocols:
Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.
Active
Active
Protocols:
Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.
Active
Active
Pull Protocol
What is
pull data
Producer
Consumer
Every JavaScript Function is a Request-Response (Pull) system.
The function is a Producer of data, and the code that calls the function is consuming it by “pulling” out a single return value from its call.
map() is an built-in synchronous, request-reponse function.
Request-Response
Request-Response
setPageSize() is a custom synchronous request-response function.
Asynchronous
Data service request is synchronous
The response is a promise of a SINGLE future value... asynchronous
const pending: Promise<User[]> = userService.loadAllUsers();
const onResponse = (users: User[]) => console.table(users);
pending.then(onResponse);
Request-Response
async function loadUsers() {
const users: User[] = await userService.loadAllUsers();
return users;
}
Asynchronous
Request-Response
Using async/await allows us to consider asynchronous requests asa synchronous responses...
Protocols
View1
Service1
UserService
loadUsers()
Pull-based
Request-Response
SearchTerm
findAllUsers( term)
Protocols
Request-Response
Live Demo
Horrible UX
Protocols
Request-Response
Protocols
Request-Response
... another Horrible UX
Reactive Approach ?
UI
Hooks
React: Reactive UI
export type FiltersProps = {
selectedFilter: string;
};
export const Filters: React.FC<FiltersProps> = ({ selectedFilter }) => {
return (
<IonItem style={inlineItem}>
<IonLabel>Show:</IonLabel>
<IonSelect value={selectedFilter} >
<IonSelectOption value="SHOW_ALL">All</IonSelectOption>
<IonSelectOption value="SHOW_ACTIVE">Active</IonSelectOption>
<IonSelectOption value="SHOW_COMPLETED">Completed</IonSelectOption>
</IonSelect>
</IonItem>
);
};
Prop changes will trigger the Filters component to react and update its associated DOM
React: Reactive UI
Parent UI Child UI
Prop changes will trigger the ToggleFilters component to react and update its associated DOM
Angular: Reactive UI
Parent UI Child UI
React: Reactive UI
export function Counter({onIncrement}) {
return (<div>
<button onClick={OnIncrement}> + </button>
</div>);
}
Here the onClick handler will react to user clicks to trigger increment action processing.
User Interaction Parent UI
User Interaction Parent UI
Angular: Reactive UI
Even Redux should be considered a Request-Reponse (pull) approach.
Redux
A solution for state management
Let's see why...
React + Redux
import { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
swith(action.type) {
case 'increment': return { count: state.count + action.payload }
}
};
export function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const onIncrement = () => dispatch({ type: 'increment', payload: 1 });
return (<div>
Count: {state.count}
<button onClick={OnIncrement}> + </button>
</div>);
}
export function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const onIncrement = () => dispatch({
type: 'increment',
payload: 1
});
return (<div>
Count: {state.count}
<button onClick={OnIncrement}> + </button>
</div>);
}
We consider OTS Redux to be a request-response solution. With the React Hook, however, it becomes reactive.
React + Redux
Reactive
We want to build
Components
{
UI
Services
React
Angular
JavaScript / TypeScript
Reactive UI Components
With event listeners and react hooks, we can build Reactive UIs.
This is only a partial solution!
UI
Hooks
Reactive UI Components
With event listeners and CD, we can build Reactive UIs.
This is only a partial solution!
UI
Hooks
UI
Change
Dectection
Reactive Pathways
?
?
What are the paths to building Reactive solutions ?
{
React framework makes this "easy"
{
Where the hard architecture work is done
Reactivity for All Layers
Business
Data
Services
Business
UI
Reactivity for All Layers
Business
Data
Services
Business
UI
Change
Detection
Reactivity for All Layers
Business
Data
Services
Business
UI
View Models
Change
Detection
Reactive Solutions
We want to "react" when:
We want to be smart and react only for specific changes in specific areas... we want use selectors to decide what changes are important.
Push Protocol
... the secret to Reactivity!
Push Protocol
What is
Push
Producer
Consumer
(active)
(passive)
Reactive Solutions
Before we can react to 1..n notifications/changes , we must first:
Before we can react to 1..n notifications/changes , we must first:
... and we must be able to observe multiple events without extra work.
Observer Pattern
Registration
Notifications
pingBtn.addEventListener('click', (e: MouseEvent => {
console.log('Clicked Ping');
})
Source
(Producer)
Destination
(Consumer)
Build a long-term, multi-event connection...
Simple
Observer Pattern
export function Counter() {
const onIncrement = (e: MouseEvent<HTMLButtonElement>) => {
console.log('Clicked');
}
return (<div>
<button onClick={OnIncrement}> Ping </button>
</div>);
}
React with event listeners
Build a long-term, multi-event connection...
Registration
Notifications
Source
(Producer)
Destination
(Consumer)
Registration
Notifications
Source
<<Observable>>
Destination
<<Observer>>
Observer Pattern
Simple API using
An Observable is a 'wrapper' around a Producer of multiple values, "pushing" them to Consumers
Observer Pattern
RxJS Implementation of
Producer
Consumer
push notifications
<<Observable>>
An Observer is a 'proxy' around a Consumer ... used to connect to a pipe connecting to an Observer
Producer
Consumer
push notifications
<<Observable>>
<<Observer>>
Observer Pattern
RxJS Implementation of
Producer
Consumer
subcribe()
next()
<<Observer>>
<<Observable>>
Observer Pattern
RxJS API using the
Producer
Consumer
subcribe()
next()
<<Observer>>
<<Observable>>
Observer Pattern
RxJS API using the
Create observable around 'document' producer
Normally you register event listeners.
document.addEventListener('click', () => console.log('Clicked!'));
Using RxJS you create an observable instead.
import { fromEvent } from 'rxjs';
fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
Observer Pattern
Comparisons using
Build connection to events for 'clicks'
Normally you register event listeners.
document.addEventListener('click', () => console.log('Clicked!'));
Using RxJS you create an observable instead.
import { fromEvent } from 'rxjs';
fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
Observer Pattern
Comparisons using
React and log click to Console
Note: the consumer is Console
Normally you register event listeners.
document.addEventListener('click', () => console.log('Clicked!'));
Using RxJS you create an observable instead.
import { fromEvent } from 'rxjs';
fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
Observer Pattern
Comparisons using
RxJS Observer Pattern
Producer
Consumer
<<Observer>>
<<Observable>>
Why is the RxJS version so powerful?
Transformations
(using Operators)
Stream
Stream
RxJS Observer Pattern
Producer
Consumer
Why is the RxJS version so powerful?
Transformations
(using Operators)
Producer
Combine
Streams
Stream
Stream
Stream
<<Observer>>
<<Observable>>
<<Observable>>
Observables allow us to change the way we build applications!
Instead of `pulling` data when needed, we will build applications that configure stream connections. With stream connections, data is `pushed` whenever it updates.
RxJS Observer Pattern
Demo #1
Observer Push
Visualize the 1-way data flows with Push-based Observer architecture:
Demo #1
Observer Push
Demo #2
Observer Push
dashboard.component.html
Demo #3
Observer Push
import { useState } from 'react';
import { useObservable } from '@mindspace-io/react';
import { makeFacade, TodosFacade } from './todos.facade';
import { VISIBILITY_FILTER as v, Todo } from './todo.model';
export type TodoHookTuple = [string, Todo[], TodosFacade];
export function useTodosFacade(): TodoHookTuple {
const [facade] = useState(() => makeFacade());
const [filter] = useObservable(facade.filter$, v.SHOW_ALL);
const [todos] = useObservable(facade.todos$, []);
return [filter, todos, facade];
}
export const TodosPage: React.FC = () => {
const [filter, todos, facade] = useTodosFacade();
const addItem = useMemo(facade.addTodo(item), [facade]);
const updateFilter = useMemo(facade.updateFilter, [facade]);
return (
<>
<div style={todoBar}>
<AddTodo onAdd={ addItem } showHint={!todos.length} />
<Filters onChange={ updateFilter } selectedFilter={filter} />
</div>
<TodoList
todos={todos}
onToggle={(item) => facade.toggleComplete(item)}
onDelete={(item) => facade.deleteTodo(item)}
/>
</>
);
};
Observer Push
Demo #1
Consider the ObservableHq which builds on concepts of 'reactive' everything to enable visualizations and live notebooks.
Reactive SaaS
Reactive Libraries
Part 2 of 5
Next:
Reactive Architectures
React Developers
By Thomas Burleson
Learn about Reactive architectures, presented in a 5-part series. This Part 1: Reactive Programming contrasts traditional request-response solutions with modern Reactive approaches that leverage the Observer pattern and RxJS.
FE Architect, Technical Lead, and Engineering Coach. Delivering web solutions using React, NextJS, Angular, and TypeScript.