StoreGroups

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

  1. Group

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();
  1. Named Group

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();
  1. Async Group

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();