Examples
Basic Toggle
Edit this snippetimport { RxBuilder } from '@reactables/core';
export const RxToggle = (initialState = false) =>
RxBuilder({
initialState,
reducers: {
toggleOn: () => true,
toggleOff: () => false,
toggle: (state) => !state,
},
});
Bind RxToggle to View

import { RxToggle } from './RxToggle';
import { useReactable } from '@reactables/react';
function App() {
const [toggleState, actions] = useReactable(RxToggle);
const { toggleOn, toggleOff, toggle } = actions;
return (
<>
<h5>Reactable Toggle</h5>
Toggle is: {toggleState ? 'On' : 'Off'}
<br />
<button onClick={toggleOn}>Toggle On</button>
<button onClick={toggleOff}>Toggle Off</button>
<button onClick={toggle}>Toggle</button>
</>
);
}
export default App;

import { RxToggle } from './RxToggle';
const [state$, actions] = RxToggle();
const { toggleOn, toggleOff, toggle } = actions;
state$.subscribe((toggleState) => {
// Update the view when state changes.
document.getElementById('toggle-state')
.innerHTML = toggleState ? 'On' : 'Off';
});
// Bind click handlers
document.getElementById('toggle-on')
.addEventListener('click', toggleOn);
document.getElementById('toggle-off')
.addEventListener('click', toggleOff);
document.getElementById('toggle')
.addEventListener('click', toggle);
Fetching Data with an Effect
Edit this snippetimport { RxBuilder, Action } from '@reactables/core';
import DataService from './data-service';
import { from, Observable, of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
export const RxFetchData = ({ dataService }: { dataService: DataService }) =>
RxBuilder({
initialState: {
loading: false,
success: false,
data: null as string | null,
error: null as any,
},
reducers: {
fetch: {
reducer: (state) => ({ ...state, loading: true }),
effects: [
(action$: Observable<Action>) =>
action$.pipe(switchMap(() => from(dataService.fetchData()))).pipe(
map((response) => ({ type: 'fetchSuccess', payload: response })),
catchError((err: unknown) =>
of({ type: 'fetchFailure' })
)
),
],
},
fetchSuccess: (state, action: Action<string>) => ({
...state,
success: true,
loading: false,
data: action.payload,
error: null,
}),
fetchFailure: (state, action) => ({
...state,
loading: false,
error: true,
success: false,
}),
},
});
Bind RxFetchData to View

import { useReactable } from '@reactables/react';
import DataService from './data-service';
import { RxFetchData } from './RxFetchData';
import './App.css';
function App() {
const [state, actions] = useReactable(RxFetchData, {
dataService: new DataService(),
});
if (!state) return;
const { loading, data } = state;
return (
<>
<div>
{data && <span>{data}</span>}
<br />
<button onClick={actions.fetch}>Fetch Data!</button>
<br />
{loading && <span>Fetching...</span>}
</div>
</>
);
}
export default App;
Composition with Reactables
Beyond creating primitives with RxBuilder
, Reactables can be combined into new ones.
Two main use cases:
- Reuse existing functionality.
- Reactable(s) reacting to changes from another reactables.
Reactables emit:
- state updates via the state observable (first item in the Reactable interface).
- actions via the actions observable (third item in the Reactable interface).
Other reactables can subscribe to these events via its sources
option (see RxConfig).
Example:
RxAuth
– handles user login.RxProfile
– loads the user profile after login.
This forms a directed acyclic graph (DAG): profile management depends on authentication but not vice versa. Both remain reusable, independent units of state logic.
DAG flow: RxAuth (login) ──▶ (loginSuccess) ──▶ RxProfile (fetch profile)
Edit this snippetRxAuth
import { Action, RxBuilder } from '@reactables/core';
import { Observable, switchMap, of, delay } from 'rxjs';
export const RxAuth = () =>
RxBuilder({
initialState: {
loggingIn: false,
loggedIn: false,
userId: null as number | null,
},
reducers: {
login: {
reducer: (state) => ({
...state,
loggingIn: true,
}),
effects: [
(login$: Observable<Action>) =>
login$.pipe(
switchMap(() =>
// Simulate API call for logging in
of({ type: 'loginSuccess', payload: { userId: 123 } }).pipe(
delay(500)
)
)
),
],
},
loginSuccess: (state, action: Action<{ userId: number }>) => ({
...state,
loggedIn: true,
userId: action.payload.userId,
loggingIn: false,
}),
},
});
RxProfile
import { Action, RxBuilder } from '@reactables/core';
import { Observable, switchMap, of, delay } from 'rxjs';
type UserProfile = {
userId: number;
userName: string;
email: string;
};
export const RxProfile = ({
sources,
}: {
sources: Observable<Action<any>>[];
}) =>
RxBuilder({
initialState: {
fetchingProfile: false,
userProfile: null as null | UserProfile,
},
sources,
reducers: {
fetchProfile: {
reducer: (state, _: Action<number>) => ({
...state,
fetchingProfile: true,
}),
effects: [
(login$: Observable<Action<number>>) =>
login$.pipe(
switchMap(({ payload: userId }) =>
// Simulate API call for fetching user profile
of({
type: 'fetchProfileSuccess',
payload: {
userId,
userName: 'Homer Simpson',
email: 'homer@simpson.com',
} as UserProfile,
}).pipe(delay(500))
)
),
],
},
fetchProfileSuccess: (_, action: Action<UserProfile>) => ({
fetchingProfile: false,
userProfile: action.payload,
}),
},
});
We can now combine the two and have RxProfile
listen to RxAuth
’s actions to detect a loginSuccess
.
RxApp (combining RxAuth
& RxProfile
)
import { combine } from '@reactables/core';
import { map } from 'rxjs/operators';
import { RxAuth } from './RxAuth';
import { RxProfile } from './RxProfile';
export const RxApp = () => {
const rxAuth = RxAuth();
// Access rxAuth's actions observable
const [, , authActions$] = rxAuth;
const fetchProfileOnLoginSuccess$ = authActions$
// Filter for login success actions
.ofTypes([authActions$.types.loginSuccess])
.pipe(
// Map action to a fetch profile action
map(({ payload: userId }) => ({
type: 'fetchProfile',
payload: userId,
}))
);
const rxProfile = RxProfile({
// Have RxProfile listen for loginSuccess and fetch profile
sources: [fetchProfileOnLoginSuccess$],
});
return combine({
auth: rxAuth,
profile: rxProfile,
});
};
Bind RxApp to View

import { useReactable } from '@reactables/react';
import { RxApp } from './RxApp';
function App() {
const [state, actions] = useReactable(RxApp);
if (!state) return;
const {
auth: { loggingIn, loggedIn, userId },
profile: { fetchingProfile, userProfile },
} = state;
return (
<>
<div>
<button onClick={actions.auth.login} disabled={loggingIn}>
Login
</button>
{loggingIn && <div>Logging In</div>}
{loggedIn && <div>User Id: {userId} logged in.</div>}
{fetchingProfile && <div>Fetching Profile...</div>}
{userProfile && (
<>
<h3>Hello {userProfile.userName}</h3>
</>
)}
</div>
</>
);
}
export default App;
Global State with Reactables
A Reactable can be used for global or shared state. It doesn’t dictate how it’s stored or accessed: in React, you can use Context
; in Angular, services; or any approach your framework provides.
Debugging
When creating a reactable primitive with RxBuilder
, a debug
option is available to console.log
all the actions and state updates occuring within that primitive.
Debug Example:
