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 simple Flux Action Creator and Flux Reducers library for @ngrx/store. Its goal is to provide simple yet type-safe experience with Flux actions. Created actions are FSA-compliant

License

NotificationsYou must be signed in to change notification settings

tblaisot/ngrx-store-fsa-helpers

Repository files navigation

A simple Flux Action Creator and Flux Reducers library for@ngrx/store. Its goal is to provide simpleyet type-safe experience with Flux actions.Created actions are FSA-compliant:

interfaceFSAAction<P>{type:string;payload?:P;error?:boolean;meta?:Object;}

It allows you to define reducers by chaining a series of handlers for different actiontypes and optionally providing an initial value.

This library is heavily inpired (with minor adaptations to make it @ngrx/store compatible) by two awesome librairies:

Table of Contents

Installation

npm install --save ngrx-store-fsa-helpersoryarn add ngrx-store-fsa-helpers

Usage

Basic

import{actionCreatorFactory}from'ngrx-store-fsa-helpers';constactionCreator=actionCreatorFactory();// Specify payload shape as generic type argument.constsomethingHappened=actionCreator<{foo:string}>('SOMETHING_HAPPENED');// Get action creator type.console.log(somethingHappened.type);// SOMETHING_HAPPENED// Create action.constaction=somethingHappened({foo:'bar'});console.log(action);// {type: 'SOMETHING_HAPPENED', payload: {foo: 'bar'}}

Async Action Creators

Async Action Creators are objects with propertiesstarted,done andfailed whose values are action creators.

started actions will have the following shape

interfaceFSAAction<P>{type:string;payload?:P;error?:boolean;meta?:Object|null;}

done actions will have the following shape

interfaceSuccessFSAAction<P,S>{type:string;payload:{params?:P;result?:S;};error?:boolean;meta?:Object;}

failed actions will have the following shape

interfaceFailureFSAAction<P,E>{type:string;payload:{params?:P;error?:E;};error:true;meta?:Object;}
import{actionCreatorFactory}from'ngrx-store-fsa-helpers';constactionCreator=actionCreatorFactory();// specify parameters and result shapes as generic type argumentsconstdoSomething=actionCreator.async<{foo:string},// parameter type{bar:number},// success type for field "result" in payload{code:number}// error type for field "error" in payload>('DO_SOMETHING');console.log(doSomething.started({foo:'lol'}));// {type: 'DO_SOMETHING_STARTED', payload: {foo: 'lol'}}console.log(doSomething.done({params:{foo:'lol'},result:{bar:42},});// {type: 'DO_SOMETHING_DONE', payload: {//   params: {foo: 'lol'},//   result: {bar: 42},// }}console.log(doSomething.failed({params:{foo:'lol'},error:{code:42},});// {type: 'DO_SOMETHING_FAILED', payload: {//   params: {foo: 'lol'},//   error: {code: 42},// }, error: true}

Actions With Type Prefix

You can specify a prefix that will be prepended to all action types. This isuseful to namespace library actions as well as for large projects where it'sconvenient to keep actions near the component that dispatches them.

// MyComponent.actions.tsimport{actionCreatorFactory}from'ngrx-store-fsa-helpers';constactionCreator=actionCreatorFactory('MyComponent');constsomethingHappened=actionCreator<{foo:string}>('SOMETHING_HAPPENED');constaction=somethingHappened({foo:'bar'});console.log(action);// {type: 'MyComponent/SOMETHING_HAPPENED', payload: {foo: 'bar'}}

Reducers

Suppose we have the setup:

import{actionCreatorFactory}from'ngrx-store-fsa-helpers';constactionCreator=actionCreatorFactory();interfaceState{name:string;balance:number;isFrozen:boolean;}constINITIAL_STATE:State={name:"Untitled",balance:0,isFrozen:false,};constsetName=actionCreator<string>("SET_NAME");functionsetNameHandler(state:State,name:string):State{return{ ...state, name};}constaddBalance=actionCreator<number>("ADD_BALANCE");functionaddBalanceHandler(state:State,addedBalance:number):State{return{ ...state,balance:state.balance+addedBalance};}constsetIsFrozen=actionCreator<boolean>("SET_IS_FROZEN");functionsetIsFrozenHandler(state:State,isFrozen:boolean):State{return{ ...state, isFrozen};}

Without Reducers Chaining

import{Action}from'@ngrx/store';import{isType}from'ngrx-store-fsa-helpers';functionreducer(state=INITIAL_STATE,action:Action):State{if(isType(action,setName)){returnsetNameHandler(state,action.payload);}elseif(isType(action,addBalance)){returnaddBalanceHandler(state,action.payload);}elseif(isType(action,setIsFrozen)){returnsetIsFrozenHandler(state,action.payload);}else{returnstate;}}

With Reducers Chaining

import{reducerWithInitialState}from'ngrx-store-fsa-helpers';constreducer=reducerWithInitialState(INITIAL_STATE).case(setName,setNameHandler).case(addBalance,addBalanceHandler).case(setIsFrozen,setIsFrozenHandler);

Everything is typesafe. If the types of the action payload and handler don't line up, thenTypeScript will complain.

The reducer builders are immutable. Each call to.case() returns a new reducer rather thanmodifying the callee.

API

Actions

actionCreatorFactory(prefix?: string, defaultIsError?: Predicate): ActionCreatorFactory

Creates Action Creator factory with optional prefix for action types.

  • prefix?: string: Prefix to be prepended to action types.
  • defaultIsError?: Predicate: Function that detects whether action is errorgiven the payload. Default ispayload => payload instanceof Error.

isType(action: Action, actionCreator: ActionCreator): boolean

Returnstrue if action has the same type as action creator. DefinesType Guardthat lets TypeScript knowpayload type inside blocks whereisType returnedtrue:

constsomethingHappened=actionCreator<{foo:string}>('SOMETHING_HAPPENED');if(isType(action,somethingHappened)){// action.payload has type {foo: string};}

Starting a reducer chain

reducerWithInitialState(initialState)

Starts a reducer builder-chain which uses the provided initial state if passedundefined as its state. For example usage, see theUsage sectionabove.

reducerWithoutInitialState()

Starts a reducer builder-chain without special logic for an initial state.undefined will be treated like any other value for the state.

Redux seems to really want you to provide an initial state for your reducers.ItscreateStore API encourages it andcombineReducers function enforces it.For the Redux author's reasoning behind this, seethisthread. For this reason,reducerWithInitialState will likely be the more common choice, but the optionto not provide an initial state is there in case you have some means ofcomposing reducers for which initial state is unnecessary.

Note that since the type of the state cannot be inferred from the initial state,it must be provided as a type parameter:

constreducer=reducerWithoutInitialState<State>().case(setName,setNameHandler).case(addBalance,addBalanceHandler).case(setIsFrozen,setIsFrozenHandler);

upcastingReducer()

Starts a builder-chain which produces a "reducer" whose return type is asupertype of the input state. This is most useful for handling a state which maybe in one of several "modes", each of which responds differently to actions andcan transition to the other modes. Many applications will not have a use forthis.

Note that the function produced is technically not a reducer because the initialand updated states are different types.

Example usage:

typeState=StoppedState|RunningState;interfaceStoppedState{    type:"STOPPED";}interfaceStartedState{    type:"STARTED";    count:number;}constINITIAL_STATE:State={type:"STOPPED"};conststartWithCount=actionCreator<number>("START_WITH_COUNT");constaddToCount=actionCreator<number>("ADD_TO_COUNT");conststop=actionCreator<void>("STOP");functionstartWithCountHandler(state:StoppedState,count:number):State{return{type:"STARTED", count};}functionaddToCountHandler(state:StartedState,count:number):State{return{ ...state,count:state.count+count};}functionstopHandler(state:StartedState):State{return{type:"STOPPED"};}conststoppedReducer=upcastingReducer<StoppedState,State>().case(startWithCount,startWithCountHandler);conststartedReducer=upcastingReducer<StartedState,State>().case(addToCount,addToCountHandler).case(stop,stopHandler);functionreducer(state=INITIAL_STATE,action:Redux.Action):State{if(state.type==="STOPPED"){returnstoppedReducer(state,action);}elseif(state.type==="STARTED"){returnstartedReducer(state,action);}else{thrownewError("Unknown state");}}

Reducer chain methods

.case(actionCreator, handler(state, payload) => newState)

Mutates the reducer such that it applieshandler when passed actions matchingthe type ofactionCreator. For examples, seeUsage.

.caseWithAction(actionCreator, handler(state, action) => newState)

Like.case(), except thathandler receives the entire action as its secondargument rather than just the payload. This is useful if you want to read otherproperties of the action, such asmeta orerror, or if you want to pass theentire action unmodified to some other function. For an example, seeUsage.

.cases(actionCreators, handler(state, payload) => newState)

Like.case(), except that multiple action creators may be provided and thesame handler is applied to all of them. That is,

reducerWithInitialState(initialState).cases([setName,addBalance,setIsFrozen],handler,);

is equivalent to

reducerWithInitialState(initialState).case(setName,handler).case(addBalance,handler).case(setIsFrozen,handler);

Note that the payload passed to the handler may be of the type of any of thelisted action types' payloads. In TypeScript terms, this means it has typeP1 | P2 | ..., whereP1, P2, ... are the payload types of the listed actioncreators.

The payload type is inferred automatically for up to four action types. Afterthat, it must be supplied as a type annotation, for example:

reducerWithInitialState(initialState).cases<{documentId:number}>([selectDocument,editDocument,deleteDocument,sendDocument,archiveDocument,],handler);

.casesWithAction(actionCreators, handler(state, action) => newState)

Like.cases(), except that the handler receives the entire action as itssecond argument rather than just the payload.

.withHandling(updateBuilder(builder) => builder)

Convenience method which applies the provided function to the current builderand returns the result. Useful if you have a sequence of builder updates (callsto.case(), etc.) which you want to reuse across several reducers.

.default(handler(state, action) => newState)

Produces a reducer which applieshandler when no previously added.case(),.caseWithAction(), etc. matched. The handler is similar to the one in.caseWithAction(). Note that.default() ends the chain and internally doesthe same as.build(), because it is not intended that the chain bemutated after calling.default().

This is useful if you have a "delegate" reducer that should be called on anyaction after handling a few specific actions in the parent.

constNESTED_STATE={someProp:"hello",};constnestedReducer=reducerWithInitialState(NESTED_STATE).case(...);constINITIAL_STATE={someOtherProp:"world"nested:NESTED_STATE};constreducer=reducerWithInitialState(INITIAL_STATE).case(...).default((state,action)=>({        ...state,nested:nestedReducer(state.nested,action),}));

.build()

Returns a plain reducer function whose behavior matches the current state of thereducer chain. Further updates to the chain (through calls to.case()) willhave no effect on this function.

There are two reasons you may want to do this:

  1. You want to ensure that the reducer is not modified further

    Calling.build() is an example of defensive coding. It prevents someonefrom causing confusing behavior by importing your reducer in an unrelatedfile and adding cases to it.

  2. You want your package to export a reducer, but not have its types dependontypescript-fsa-reducers

    If the code that defines a reducer and the code that uses it reside inseparate NPM packages, you may run into type errors since the exportedreducer has typeReducerBuilder, which the consuming package does notrecognize unless it also depends ontypescript-fsa-reducers. This isavoided by returning a plain function instead.

Example usage:

constreducer=reducerWithInitialState(INITIAL_STATE).case(setName,setNameHandler).case(addBalance,addBalanceHandler).case(setIsFrozen,setIsFrozenHandler).build();

About

A simple Flux Action Creator and Flux Reducers library for @ngrx/store. Its goal is to provide simple yet type-safe experience with Flux actions. Created actions are FSA-compliant

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp