Groups
Groups are collections of emitters, states, and other groups that are unified under a specific feature. They help organize related reactive sources into a cohesive structure, ensuring proper initialization and completion of all items as a single unit.
List of Groups
The group collects all subsequently created emitters, states, and groups until
group.markAsReady()
is called.
The group can be used as an alternative to the store when lifecycle hooks are not required, or when extending the Store class is not feasible due to other class inheritance.
import {state, emitter, group, asyncGroup} from '@bitfiber/rx';
// Defines a group that collects emitters, states, and groups
const myGroup = group();
// Add group items here
const someState = state<string>('');
const someEmitter = emitter<number>();
const req = asyncGroup<string, number, number>();
// Marks the group as ready, indicating that all group items have been defined
myGroup.markAsReady();
// Initializes the group and all its items
myGroup.initialize();
// Completes the group and all its items
myGroup.complete();
The named group extracts emitters, states, and groups from the provided object and adds them to the group. Each item is accessible by the key used in the object.
import {switchMap} from 'rxjs';
import {state, emitter, namedGroup, transmit} from '@bitfiber/rx';
// Defines a named group
const myGroup = namedGroup(
{
// Add group items here
launch: emitter<void>(),
data: state<number>(0),
},
({launch, data}) => {
// Add interaction logic in the 'onInit' callback
launch
// Defines a side effect for data loading
.effect(switchMap(() => getData('api/data').pipe(transmit(data))));
}
);
// Initializes the group and all its items
myGroup.initialize();
// Triggers the loading process
myGroup.launch.emit();
// Completes the group and all its items
myGroup.complete();
The async group manages the lifecycle of asynchronous actions, providing emitters for launching actions, handling success, dealing with failures, and managing the state of these actions.
import {switchMap} from 'rxjs';
import {state, asyncGroup, transmit} from '@bitfiber/rx';
interface Product {
id: number;
name: string;
price: number;
}
interface ProductsState {
products: Product[];
isLoading: boolean;
}
// Defines an async group for managing product loading
const productsReq = asyncGroup<number, Product[], Error>((group, {
launch, success, fail, finish,
}) => {
group
// Keeps cached data for 120 seconds, with a maximum entry count of 10
.useCache(120, 10);
launch
// Defines a side effect for loading products
.effect(switchMap(page => productsService.get(`api/products?page=${page}`)
.pipe(transmit(group))));
success
// Performs a tap callback each time the request succeeds
.tap(data => console.log(data));
fail
// Performs a tap callback each time the request fails
.tap(error => console.log(error));
finish
// Performs a tap callback each time the request either fails or succeeds
.tap(() => console.log('Request has been finished'));
}, []);
// Provides the main state
const data = state<ProductsState>({products: [], isLoading: false}, s => s
// Receives request success data
.receive(productsReq.success, (products, state) => ({...state, products}))
// Starts the products loading process
.receive(productsReq.state, ({inProgress}, state) => ({...state, isLoading: inProgress}))
);
// Starts the products loading process
productsReq.launch.emit(1);
Creating a Custom Group
To create a custom group, extend the AbstractGroup
class:
import {AbstractGroup, emitter, state, asyncGroup} from '@bitfiber/rx';
class NotificationsGroup extends AbstractGroup {
// Define your group items here
}
Adding Group Items
Within the group class, define the group items you need, such as emitters, states, or groups:
class NotificationsGroup extends AbstractGroup {
// Defines an async group for managing notifications loading
notificationsReg = asyncGroup<void, string[], Error>();
// Defines a state to store the current notifications
notifications = state<string[]>([]);
}
Marking Group as Ready
To finalize the group setup and indicate that all group items have been defined,call
the markAsReady
method:
class NotificationsGroup extends AbstractGroup {
notificationsReg = asyncGroup<void, string[], Error>();
notifications = state<string[]>([]);
// Marks the group as ready
#ready = this.markAsReady();
}
Adding Interaction Logic
Use the executeInnerDeferredActions
method to add interaction logic:
class NotificationsGroup extends AbstractGroup {
req = asyncGroup<void, string[], Error>();
data = state<string[]>([]);
#ready = this.markAsReady();
// Add interaction logic here
protected override executeInnerDeferredActions(): void {
this.req.launch
// Launches notifications request when the launch emitter emits
.effect(switchMap(() => getNotifications().pipe(transmit(this.req))));
this.data
// Updates data state with successful results
.receive(this.req.success);
}
}
Adding Factory Function
To maintain a consistent style in group implementations, define a factory function for creating the group. This approach provides a standardized way to initialize groups and optionally apply additional setup logic:
export function notificationsGroup(
onInit?: (group: NotificationsGroup, sameGroup: NotificationsGroup) => void,
): NotificationsGroup {
const group = new NotificationsGroup();
return onInit ? group.onInit(onInit) : group;
}
Usage the group
Create and initialize the group using the factory function:
// Defines a custom group
const customGroup = notificationsGroup();
// Initializes the group and all its items
customGroup.initialize();
// Triggers notifications loading
customGroup.req.launch.emit();
// Subscribes to notifications
customGroup.data.$.subscribe(notifications => {
console.log('Received notifications:', notifications);
});
// Retrieves notifications synchronously
const notifications = customGroup.data();
console.log('Current notifications:', notifications);
// Completes the group and all its items
customGroup.complete();