@studiohyperdrive/ngx-store
TypeScript icon, indicating that this package has built-in type declarations

17.3.0 • Public • Published

NGX-Store

NGX-store aims to reduce the boilerplate that comes with @ngrx, @ngrx/effects and @ngrx/entity; whilst still including the benefits of said packages.

Each of the provided utils can be used individually, but we strongly advice to use all three utils as a coherent unit to get the best developers experience.

Installation

Install the package first:

npm install @studiohyperdrive/ngx-store

Concept

As mentioned before, ngx-store works in tandem with @ngrx. The package aims to optimize the workflow for (complex) projects that currently already use this redux implementation.

With this in mind, our goal is to make this package as flexible as possible. We are aware that existing projects often already have their own approach to their store setup, and therefor we try to provide solutions that can be used when needed. Whilst we strongly suggest using the entire system for the optimal approach, several of our utils are entirely optional. The use of handleEffect, dispatchDataToStore and the StoreService is never mandatory and all utils are opt-in at any stage.

Because of this approach, our implementation has to take into account these constraints and will therefore deviate from the standard redux implementation.

Store utils

createStoreAssets

In order to reduce the boilerplate of actions, reducers and selectors; and in the effort of creating a coherent store setup, NGX-Store uses the createStoreAssets util to setup a slice of the store.

A store slice can consists of multiple sub-slices, each one representing data we wish to save in the store. This data can either be in the form of an array, for which we use EntityStoreAssets; and other primitives like strings, numbers, objects, records, etc. which we save as BaseStoreAssets.

When using the createStoreAssets, we first define a type or interface which will represent the store slice we wish to create. This interface will be used by the util internally and is therefor required. An example of this type would be:

    type ExampleStoreAssets = {
	    channel: BaseStoreAssets<DataType>;
	    videos: EntityStoreAssets<DataType>;
    };

Once we have this type defined, we can use it in the createStoreAssets util and assign the correct generators to each sub-slice. These generators will provide a series of default actions, reducers and selectors which you can use throughout your application.

When using the createBaseStoreAssets generator, we are presented with the following actions and corresponding reducers:

Action
set Sets the provided data in the store
loading Sets the loading state of provided data in the store
error Sets the error state of the provided data in the store
clear Clears the earlier set data from the store
effects.set Dispatches a trigger for a set effect

On top of that, we get a series of default selectors corresponding with the aforementioned actions.

Selector
select Selects the provided data from the store
selectLoading Selects the loading state of provided data from the store
selectError Selects the error state of the provided data from the store
selectErrorMessage Selects the provided error data of the provided data from the store

When using createEntityStoreAssets, we use @ngrx/entity as a base to save data in array form. We strongly suggest using EntityStoreAssets for arrays, as @ngrx/entity has several optimized methods to handle arrays.

Using createEntityStoreAssets will provide you with the following actions and corresponding reducers.

Action
set Sets the provided data in the store
add Adds the provided data to the existing data in the store
update Updates the provided data in the existing data in the store
delete Deletes the provided data from the existing data in the store
loading Sets the loading state of provided data in the store
error Sets the error state of the provided data in the store
clear Clears the earlier set data from the store
effects.set Dispatches a trigger for a set effect
effects.add Dispatches a trigger for an add effect
effects.delete Dispatches a trigger for a delete effect
effects.update Dispatches a trigger for an update effect

On top of the provided actions and reducers, the util also provides the following selectors.

Selector
selectAll Selects the provided data from the store
selectLoading Selects the loading state of provided data from the store
selectError Selects the error state of the provided data from the store
selectErrorMessage Selects the provided error data of the provided data from the store

Effects

Actions

To support @ngrx-effects the generator automatically generates a series of actions that can be used to handle effects. These can be found under the effects property, and match the set, add, update and delete actions.

In order to define the type of the payload of the effect actions, we can pass a secondary type to both the BaseStoreAssets and EntityStoreAssets (and their respective generators).

A short example can be found here

interface UserStore extends StoreFlowAssets {
	users: EntityStoreAssets<User, { set: void; add: string }>;
	paging: BaseStoreAssets<string>;
}

export const { actions, reducers, selectors } = createStoreAssets<UserStore>('users', [
	{
		subSlice: 'users',
		generator: createEntityAdapterStoreAssets<User, { set: void; add: string }>,
	},
	{
		subSlice: 'paging',
		generator: createBaseStoreAssets<string>,
	},
]);

handleEffect

As additional support to effects, the package provides a handleEffect operator which will automatically take care of any of the provided actions. By defining which sub-slice we wish to use, the action we wish to handle and the corresponding data source, the handleEffect operator will perform all the necessary actions required.

A short example can be found here

	public fetchUsers$ = createEffect(() => {
		return this.actions$.pipe(
			handleEffect<User[]>(actions.users, 'set', this.userService.fetchUsers)
		);
	});

dispatchDataToSTore

An additional util that works in tandem with the aforementioned store assets is the dispatchDataToStore util. Using the assets, the util will automatically handle the loading and error state of the provided data.

A short example can be found here

    public  getChannel():  Observable<DataType> {
	    return  dispatchDataToStore(
		    actions.channel,
		    this.httpClient.get<DataType>('test'),
		    this.store
		    );
    }

The util returns an Observable for easy further chaining throughout the application.

StoreService

Just as the aforementioned dispatchDataToStore util, the StoreService abstraction works best in tandem with the createStoreAssets util.

This abstraction provides easy access to the selectors that were generated by the assets. These are aimed to reduce the boilerplate and create a coherent store setup throughout your entire application. By passing the selector object to the methods, the abstraction will automatically handle the selection.

method
selectFromStore Selects the provided data from the store
selectLoadingFromStore Selects the loading state of provided data from the store
selectErrorFromStore Selects the error state of the provided data from the store
selectErrorMessageFromStore Selects the provided error data of the provided data from the store

On top of that, the StoreService can automatically generate observables for each sub-slice in the provided store. By providing the store interface to the StoreService via generics and passing the generated selectors to the constructor, a state object is generated including all the necessary observables.

A short example would be:

interface StoreState extends StoreFlowAssets {
	isCompleted: BaseStoreAssets<boolean>;
}

export const { actions, reducers, selectors } = createStoreAssets<StoreState>('state', [
	{
		subSlice: 'isCompleted',
		generator: createBaseStoreAssets<boolean>,
	},
]);

@Injectable()
export class StoreStateService extends StoreService<StoreState> {
	constructor(protected readonly store: Store) {
		super(store, selectors);
	}
}

const storeStateService = new StoreStateService(store);

const state = storeStateService.state;
// Contains: isCompleted$, isCompletedLoading$, isCompletedError$ and isCompletedErrorMessage$

build information

This project has been build with:

  • Angular CLI : 16.1.4
  • Angular: 16.1.5
  • nodejs: 18.17.0
  • npm: 9.6.7

For a complete list of packages and version check out the package.json file.

Team

This bundle of tools has been created and is maintained by Studio Hyperdrive.

Contributors:

Readme

Keywords

none

Package Sidebar

Install

npm i @studiohyperdrive/ngx-store

Weekly Downloads

77

Version

17.3.0

License

MIT

Unpacked Size

152 kB

Total Files

46

Last publish

Collaborators

  • wouterheirstrate
  • ekincia
  • bertyhell
  • shd-developer
  • ian-emsens-shd
  • jeroenvalcke
  • tom-odb
  • brilniels
  • thomasbormans
  • sanderh
  • laurenspeeters
  • rednas
  • denisvalcke
  • ibenvandeveire