Skip to main content

Store

Stores serve as a central hub for managing a specific slice of the application's state. A store may be used globally but can also be scoped at a component or service level. Rather than using a single store for the entire application, the state can be distributed across multiple isolated stores. Each store could be responsible for managing state related to a specific domain entity or a feature.

A store encapsulates a WritableSignal as state. It exposes the functions set, update, and mutate for performing state modifications, as well as the state as readonly signal. While Signalstory offers a variety of features for your everyday needs, it goes beyond that by introducing concepts and abstractions to help you build a maintainable and scalable architecture. Some of these abstractions include:

  • Command: A structured approach to exposing state-modifying methods.
  • Query: Patterns and utilities facilitating testable multi-store queries.
  • Effect Objects: Encapsulation of side effects and action flows into maintainable and testable standalone objects with a single purpose. Use meta attributes to control addtional behaviors.
  • Events: Synchronous propagation of events across stores.
info

Please note that these abstractions and features are entirely optional and not mandatory for signalstory to function. Feel free to cherry-pick the functionalities that suit your needs!

Create an Injectable Store Service

Stores can be defined as injectable concrete classes extending the generic Store class, that can be injected into Angular components, services, and other classes.

export interface UserData {
name: string;
birthday: Date;
}

export interface UserUI {
isSelected: boolean;
}

export type UserState = (UserData & UserUI)[];

@Injectable({ providedIn: 'root' })
export class UserStore extends Store<UserState> {
constructor() {
super({ initialState: [] });
}

// Expose functions to modify the state
public selectUser(name: string): void {
this.mutate(state => {
const userToSelect = state.find(user => user.name === name);
if (userToSelect) {
userToSelect.isSelected = true;
}
});
}

// Expose functions to query the state
public get selectedUsers(): Signal<UserState> {
return computed(() => this.state().filter(user => user.isSelected));
}
}

Create a Dynamic Store

signalstory also provides the flexibility to create generic store instances dynamically at runtime, which may be useful for certain scenarios.

const counterStore = new Store<number>({ initialState: 5 });
counterStore.update(state => state++, 'Increment');
console.log(counterStore.state());