Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A tiny, yet powerful state management library for Angular

License

NotificationsYou must be signed in to change notification settings

sebholstein/tinystate

Repository files navigation

TinyState
A tiny, yet powerful state management library for Angular inspired byUnstated.
Created by@Sebholstein

Build Statuscodecovnpm versionSupported Angular versions: 6+License: MIT


Introduction

State management in every component-based application is hard. TinyState tries to solve the following problem:

Sharing state between components as simple as possible and leverage the good parts of component state and Angular`s dependency injection system.

Demo

Demo onStackblitz.io

Installation

yarn add @tinystate/core# ornpm install @tinystate/core

Loading the module in the app/root module

import{TinyStateModule}from'@tinystate/core';@NgModule({imports:[CommonModule,TinyStateModule.forRoot()]})classAppModule{}

Example

import{Container}from'@tinystate/core';exportinterfaceCounterState{count:number;}/** * A Container is a very simple class that holds your state and some logic for updating it. * The shape of the state is described via an interface (in this example: CounterState). */exportclassCounterContainerextendsContainer<CounterState>{getInitialState():CounterState{return{count:0};}increment(increment:number){this.setState(state=>({count:state.count+increment}));}decrement(decrement:number=1){this.setState(state=>({count:state.count-decrement}));}}@Component({selector:'my-component',template:`    <h1>      Counter: {{ counter$ | async }}    </h1>    <button (click)="increment()">Increment</button>    <button (click)="decrement()">Decrement</button>  `,providers:[CounterContainer]})exportclassMyComponent{counter$:Observable<number>=this.counterContainer.select(state=>state.count);constructor(privatecounterContainer:CounterContainer){}increment(){this.counterContainer.increment(1);}decrement(){this.counterContainer.decrement();}}

Global state

The example shown above creates aCounterContainer instance for theMyComponent and is also injectable for allchild components of theMyComponent.

If you have global state that should be injectable in all your components, add theprovidedIn: 'root' option to the@Injectable decorator of the Container:

@Injectable({providedIn:'root'})classCounterContainer{}

With the configuration show above, you can inject theCounterContainer container in every component of your application.

Testing containers

Testing containers is really easy. Let's say we want to write a test for the following container:

import{Container}from'@tinystate/core';exportinterfaceCounterState{count:number;}exportclassCounterContainerextendsContainer<CounterState>{getInitialState():CounterState{return{count:0};}increment(){this.setState(state=>({count:state.count+1}));}}

Here's an example of a possible test with Jasmine:

import{CounterContainer}from'./counter.container';import{TestBed,inject}from'@angular/core/testing';describe('CounterContainer',()=>{beforeEach(()=>{TestBed.configureTestingModule({providers:[CounterContainer]});});it('should have an initial count state of 0',inject([CounterContainer],(container:CounterContainer)=>{letcount:number|undefined;container.select(s=>s.count).subscribe(s=>(count=s));expect(count).toEqual(0);}));it('should increment the count by one when calling increment',inject([CounterContainer],(container:CounterContainer)=>{letcount:number|undefined;container.select(s=>s.count).subscribe(s=>(count=s));container.increment();expect(count).toEqual(1);}));});

Testing components that use containers

Let's write a test for the following component:

@Component({selector:'my-component',changeDetection:ChangeDetectionStrategy.OnPush,template:`    <div>{{ count$ | async }}</div>    <button (click)="increment()">inc</button>  `})classMyComponent{count$:Observable<number>=this.counterContainer.select(s=>s.count);constructor(privatecounterContainer:CounterContainer){}increment(){this.counterContainer.increment();}}
describe('MyComponent',()=>{letcounterContainer:CounterContainer;beforeEach(async(()=>{counterContainer=jasmine.createSpyObj<CounterContainer>('CounterContainer',['increment','select']);returnTestBed.configureTestingModule({declarations:[MyComponent],providers:[{provide:CounterContainer,useValue:counterContainer}]}).compileComponents();}));it('should increment via the counter',()=>{constfixture=TestBed.createComponent(MyComponent);fixture.debugElement.query(By.css('.inc-count')).triggerEventHandler('click',null);expect(counterContainer.increment).toHaveBeenCalledTimes(1);});});

Redux Devtools Support

To enable support for the Redux Devtools Extension, add the following module to your root NgModule:

import{TinyStateModule,ReduxDevtoolsPluginModule}from'@tinystate/core';@NgModule({imports:[CommonModule,TinyStateModule.forRoot(),ReduxDevtoolsPluginModule.forRoot()],providers:[CounterContainer]})classAppModule{}

TinyState hasn't the concept of Actions. So the action name will always beNO_NAME. But you will see how the state of all your containers change, which is even without action names useful for debugging:

Redux Devtools Demo Gif

FAQ

When should I use TinyState?

Do you have a global state or a state that is needed in several components that you want to share between them and think that solutions like NGRX or Redux are a way too heavy for your simple use case? - then TinyState could be for you.

TinyState is not a solution that should be seen as an alternative to NGRX or Redux because these projects are trying to solve different problems than TinyState wants to solve.

IMO local component state is totally fine as long as it works for you. So choose the right tool for the right job.

Can I use the action/reducer pattern with TinyState?

Nope. The goal of this project is to keep sharing state between components simple. If you think your state is too complex/big or you want a replayable, fully predictable state container, you should consider usingNGRX,NGXS orRedux.

What are the differences between TinyState andUnstated?

  • Unstated supports React - TinyState supports Angular.
  • TinyState uses RxJS as the base for all the state handling whereas Unstated uses plain objects. RxJS plays very well together with Angular and allows powerful streaming transformations.
  • Unstated uses the React Context API and a self-implemented Injection pattern whereas TinyState uses Angular's built-in Hierarchical Dependency Injectors to create/assign Container instances to component hierarchies.
  • TinyState supports Redux Devtools and has a plugin API.

About

A tiny, yet powerful state management library for Angular

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors2

  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp