simplr-flux v2.3.1
simplr-flux
Flux implementation that enables you to handle actions by their type, without nasty and never ending if or switch trees.
The package is most useful when used with TypeScript and React.
Get started
npm install simplr-flux --save
Concepts
Basic concepts of SimplrFlux
are no different from original flux concepts.
We also recommend to follow the best practises proposed by original flux.
Actions
Actions define the internal API of your application. They capture the ways in which anything might interact with your application.
In SimplrFlux
action is basically a class with all necessary data provided.
// action with no data provided
export class CountIncrementedAction { }
// action with additional data provided
export class CountChangedAction {
constructor(private count: number) {}
public get Count() {
return this.count;
}
}
Dispatcher
The dispatcher receives actions and dispatches them to stores that have registered with the dispatcher.
To receive the actions you have to register them specifically.
Dispatching is usually performed in ActionCreators.
import { Dispatcher } from "simplr-flux";
import {
CountChangedAction
} from "./counter-actions";
export function CountChanged(count: number) {
Dispatcher.dispatch(new CountChangedAction(count));
}
Action creators
An action creator is a set of functions for dispatching actions.
You may choose the way to aggregate these functions,
but we recommend using exported functions within a single namespace when using TypeScript
.
import { Dispatcher } from "simplr-flux";
import {
CountIncrementedAction,
CountDecrementedAction,
CountResetAction,
CountChangedAction
} from "./counter-actions";
export namespace CounterActionsCreators {
export function CountIncremented() {
Dispatcher.dispatch(new CountIncrementedAction);
}
export function CountDecremented() {
Dispatcher.dispatch(new CountDecrementedAction);
}
export function CountReset() {
Dispatcher.dispatch(new CountResetAction);
}
export function CountChanged(count: number) {
Dispatcher.dispatch(new CountChangedAction(count));
}
}
Stores
A store is what holds the data of an application. Stores will register with the application's dispatcher so that they can receive actions.
Main difference between store types in SimplrFlux
is state management.
In accordance with your data structure you may choose a type of store that best fits your data structure.
ReduceStore
ReduceStore state
can be any object
and has no structure constraints.
The only way to change state
of ReduceStore
is actions handlers
.
Actions are registered in store's constructor
using protected method registerAction
which takes an action
class and a handler
function as arguments (check API
section).
Updated store state should be returned as a result of a handler
function.
Accessing data can be accomplished using public method getState(): StoreState
or you may implement additional public getters yourself (e. g. public getter Count()
in an example bellow).
import { ReduceStore } from "simplr-flux";
import {
CountDecrementedAction,
CountIncrementedAction,
CountResetAction,
CountChangedAction
} from "./counter-actions";
interface StoreState {
Count: number;
}
class CounterReduceStoreClass extends ReduceStore<StoreState> {
constructor() {
super();
this.registerAction(CountDecrementedAction, this.onCountDecremented.bind(this));
this.registerAction(CountIncrementedAction, this.onCountIncremented.bind(this));
this.registerAction(CountResetAction, this.onCountReset.bind(this));
this.registerAction(CountChangedAction, this.onCountChanged.bind(this));
}
private onCountDecremented(action: CountDecrementedAction, state: StoreState): StoreState {
return {
Count: state.Count - 1
};
}
private onCountIncremented(action: CountIncrementedAction, state: StoreState): StoreState {
return {
Count: state.Count + 1
};
}
private onCountReset(action: CountResetAction, state: StoreState): StoreState {
return this.getInitialState();
}
private onCountChanged(action: CountChangedAction, state: StoreState): StoreState {
return {
Count: action.Count
};
}
getInitialState(): StoreState {
return {
Count: 0
};
}
public get Count(): number {
return this.getState().Count;
}
}
export const CounterReduceStore = new CounterReduceStoreClass();
Full working example can be found in examples/reduce-store-example
.
MapStore
MapStore
is a key-value store with a state of Immutable.Map that keeps key-value pairs of the same value type.
To get values from MapStore
you should use public methods get
for a single item or
getAll
for multiple ones.
Values from MapStore
are returned in an Item
object with:
Status
property for item loading status (checkAPI
forItemStatus
);Value
for actual value of a requested item.
If values requested with getAll
items will be returned in an Immutable.Map<string, Items>
.
Once get
or getAll
is called, MapStore
invokes method requestData
where pass all not cached keys as an argument.
requestData
is an abstract method that must be implemented when creating a MapStore
. It fetches data from server or other data source and place it into a MapStore
.
Be aware that requestData
is always called asynchronously. MapStore
throttles requests to avoid large amount of requests at a single moment of time. Time between portion of requests can be set using protected property RequestsIntervalInMs
.
import { MapStore } from "simplr-flux";
export interface Post {
userId: number;
id: number;
title: string;
body: string;
}
type PostsDictionary = { [key: string]: Post };
class PostsStoreClass extends MapStore<Post> {
protected async requestData(keys: string[]): Promise<PostsDictionary> {
let promises: Promise<void>[] = [];
let postsDictionary: PostsDictionary = {};
for (let key of keys) {
const promise = fetch(`https://jsonplaceholder.typicode.com/posts/${key}`)
.then(data => data.json())
.then((data: Post) => {
postsDictionary[key] = data;
});
promises.push(promise);
}
await Promise.all(promises);
return postsDictionary;
}
}
export const PostsStore = new PostsStoreClass();
Full working example can be found in examples/map-store-example
.
DataStore
DataStore
is another key-value store with a state of Immutable.Map. Not like MapStore
it can hold values of different types.
To get values from DataStore
you should use a protected method getValueFromState
in public getters of your own implementation.
getValueFromState
is a helper method that takes a unique key of a value to hold and promiseFactory
- function to resolve this value.
Values resolved by getValueFromState
are returned in an Item
object with.
import { DataStore } from "simplr-flux";
import { Abstractions } from "simplr-flux";
import * as path from "path";
export interface Address {
HouseNumber: string;
City: string;
Country: string;
PostCode: string;
Street: string;
}
export interface PersonalData {
Name: string;
LastName: string;
PhoneNumber: string;
}
const JSONS_FOLDER_NAME = "assets";
const ADDRESS_KEY = "address";
const PERSONAL_DATA_KEY = "personal-data";
class ContactDataStoreClass extends DataStore {
private constructPath(fileName: string) {
return path.join(__dirname, JSONS_FOLDER_NAME, fileName);
}
private getAddress = async () => {
try {
return await SystemJS.import(this.constructPath("address.json!"));
} catch (error) {
console.error(error);
}
}
public GetAddress(noCache?: boolean): Abstractions.Item<Address> {
return this.getValueFromState<Address>(ADDRESS_KEY, this.getAddress, noCache);
}
private getPersonalData = async () => {
try {
return await SystemJS.import(this.constructPath("personal-data.json!"));
} catch (error) {
console.error(error);
}
}
public get PersonalData(): Abstractions.Item<PersonalData> {
return this.getValueFromState<PersonalData>(PERSONAL_DATA_KEY, this.getPersonalData);
}
public InvalidatePersonalData() {
this.invalidateCache(PERSONAL_DATA_KEY);
}
}
export const ContactDataStore = new ContactDataStoreClass();
Full working example can be found in examples/data-store-example
.
Container
To keep Views
up to date with the latest data from stores we recommend you to use flux/utils
Container.
import * as React from "react";
import { Item, ItemStatus } from "simplr-flux/abstractions";
import { Container } from "flux/utils";
import { PostsStore, Post } from "./posts-store";
import { PostView } from "./post-view";
interface State {
Post: Item<Post>;
}
interface Props {
id: string;
}
class PostsContainerClass extends React.Component<Props, State> {
static getStores() {
return [PostsStore];
}
static calculateState(state: State, props: Props): State {
return {
Post: PostsStore.get(props.id)
};
}
render() {
switch (this.state.Post.Status) {
case ItemStatus.Init: return <div>Post loading initialized.</div>;
case ItemStatus.Pending: return <div>Post loading pending.</div>;
case ItemStatus.Loaded: {
return <PostView
id={this.state.Post.Value!.id}
title={this.state.Post.Value!.title}
body={this.state.Post.Value!.body}
/>;
}
case ItemStatus.NoData: return <div>No post found.</div>;
case ItemStatus.Failed: return <div>Failed to load post.</div>;
}
}
}
export const PostsContainer = Container.create(PostsContainerClass, { withProps: true });
API
class DispatcherBuilder
A class to create Dispatcher
instances.
import { DispatcherBuilder } from "simplr-flux";
DispatcherBuilder
extends flux.Dispatcher
. You can check it's documentation here.
public dispatch<TAction>(dispatcherMessage: TAction): void
Dispatches a payload to all registered callbacks.
Argument | Type | Description |
---|---|---|
dispatcherMessage | TAction | Instance of class. |
register(callback: (payload: TPayload) => void): string
Registers a callback that will be invoked with every payload sent to the dispatcher.
Returns a string token to identify the callback to be used with waitFor() or unregister.
Argument | Type | Description |
---|---|---|
callback | TPayload | A callback to be invoked with every dispatched payload. |
unregister(id: string): void
Unregisters a callback with the given ID token.
Argument | Type | Description |
---|---|---|
id | string | Callback token. |
waitFor(IDs: string[]): void
Waits for the callbacks with the specified IDs to be invoked before continuing execution of the current callback.
This method should only be used by a callback in response to a dispatched payload.
Argument | Type | Description |
---|---|---|
ids | string[] | Array of callbacks tokens specified to be invoked before continuing execution of the current callback. |
isDispatching(): boolean
Returns boolean value that defines if this dispatcher is currently dispatching.
Dispatcher instance
SimplrFlux
exports instance of a dispatcher:
import { Dispatcher } from "simplr-flux";
It is recommended to have only one Dispatcher
instance per app, so you don't need to create Dispatcher
instance yourself,
it's already created by SimplrFlux
.
Abstractions
import { Abstractions } from "simplr-flux";
enum ItemStatus
Item status in MapStore
and DataStore
states.
export const enum ItemStatus {
Init = 0,
Pending = 8,
Loaded = 16,
NoData = 64,
Failed = 128
}
class Item<T>
T
- item type.
constructor(status?: ItemStatus, value?: T)
Creates an instance of Item.
Argument | Type | Initial value | Description |
---|---|---|---|
status | ItemStatus | ItemStatus.Init | Item status. |
value | T | undefined | Item value. |
public readonly Status: ItemStatus
Item status.
public readonly Value: Readonly<T> | undefined
Item value.
export type Items<T> = Immutable.Map<string, Item<T>>
T
- item type.
Type of items in MapStore
state.
Documentation of Immutable.Map
.
abstract class ReduceStore<TState>
TState
- store state.
import { ReduceStore } from "simplr-flux";
ReduceStore
extends FluxReduceStore from Facebook Flux
.
Documentation of FluxReduceStore
can be found here.
constructor(dispatcher?: Flux.Dispatcher<DispatcherMessage<any>>)
Creates an instance of ReduceStore.
Argument | Type | Description |
---|---|---|
dispatcher | Flux.Dispatcher<DispatcherMessage<any>> | Dispatcher instance. |
protected registerAction<TClass>(action: Function, handler: ActionHandler<TClass, TState>): void
Registers specified action handler in this store.
TClass
- action class.
TState
- store state.
export type ActionHandler<TClass, TState> = (action: TClass, state: TState) => TState | void;
Argument | Type | Description |
---|---|---|
action | Function | Action class function (in TypeScript - class itself). |
handler | ActionHandler<TClass, TState> | Action handler function. |
abstract getInitialState(): TState;
TState
- store state.
Constructs the initial state for this store. This is called once during construction of the store.
getState(): TState
Getter that exposes the entire state of this store.
protected cleanUpStore(): void
Cleans up all store data. This method is only available in the middle of a dispatch!
protected get currentSession(): number
Returns current session timestamp.
public getDispatcher(): DispatcherBuilder
Returns the dispatcher for this store.
protected storeWillCleanUp: undefined | StoreWillCleanup<TState>
export type StoreWillCleanup<TState> = () => void | TState;
TState
- store state.
Method is invoked immediately before a store begins to clean the state. It's called in the middle of a dispatch cycle. If state returned in this method, it's used for initial state.
protected shouldHandleAction(action: Object, state: TState): boolean
Checks if action should be handled. By default always returns true.
Argument | Type | Description |
---|---|---|
action | Object | Action payload data. |
state | TState | Updated store state. |
abstract class MapStore<TValue>
import { MapStore } from "simplr-flux";
MapStore
extends ReduceStore
of SimplrFlux
.
Documentation of ReduceStore can be found here.
State of MapStore
is a Items
of type TValue. Check API section for Abstractions Items
.
TValue
- type of MapStore
item value.
constructor(dispatcher?: Flux.Dispatcher<DispatcherMessage<any>>)
Creates an instance of MapStore.
Argument | Type | Description |
---|---|---|
dispatcher | Flux.Dispatcher<DispatcherMessage<any>> | Dispatcher instance. |
protected abstract requestData(ids: string[], onSuccess?: OnSuccess<TValue>, onFailed?: OnFailure): Promise<{ [id: string]: TValue }> | void
API call to get data from server or other data source.
Returns dictionary of resolved values.
Argument | Type | Description |
---|---|---|
ids | string[] | List of requesting ids. |
onSuccess | OnSuccess | Success callback with items that succeeded. |
onFailed | OnFailure | Failure callback with items statuses. |
TValue
- type of MapStore
item value.
OnSuccess
and OnFailure
can be imported from Contracts
.
import { Contracts } from "simplr-flux";
// Contracts.OnSuccess
// Contracts.OnFailure
public get(key: string, noCache: boolean = false): Item<TValue>
TValue
- type of MapStore
item value.
Get the value of a particular key.
Returns undefined Value
and status if the key does not exist in the cache (check Item
).
Argument | Type | Description |
---|---|---|
key | string | Requested item key. |
noCache | boolean | Update cached item from the server or other data source. |
public getAll(keys: string[], prev?: Items<TValue>, noCache?: boolean): Items<TValue>
public getAll(keys: Immutable.List<string>, prev?: Items<TValue>, noCache?: boolean): Items<TValue>
public getAll(keys: Immutable.Set<string>, prev?: Items<TValue>, noCache?: boolean): Items<TValue>
public getAll(keys: Immutable.OrderedSet<string>, prev?: Items<TValue>, noCache?: boolean): Items<TValue>
TValue
- type of MapStore
item value.
Items
- check API section Abstractions Items
.
Gets an array of keys and puts the values in a map if they exist, it allows providing a previous result to update instead of generating a new map.
Providing a previous result allows the possibility of keeping the same reference if the keys did not change.
Returns requested data map.
Argument | Type | Description |
---|---|---|
keys | string[] | Immutable.List\<string> | Immutable.Set\<string> | Immutable.OrderedSet\<string> | Requested keys list in an Array or an ImmutableList . |
prev | Items\ | Previous data map merged with new data map. |
noCache | Items\ | Update cached item from the server or other data source. |
public has(key: string): boolean
Checks if the cache has a particular key.
Returns boolean value that defines whether cache has a particular key.
Argument | Type | Description |
---|---|---|
key | string | Requested item key. |
public Prefetch(key: string, noCache: boolean = false): void
Prefetch item by key.
Argument | Type | Description |
---|---|---|
key | string | Requested item key. |
noCache | boolean | Update cached item from the server or other data source. |
public PrefetchAll(keys: string[], noCache: boolean = false): void
public PrefetchAll(keys: Immutable.List<string>, noCache?: boolean): void
public PrefetchAll(keys: Immutable.Set<string>, noCache?: boolean): void
public PrefetchAll(keys: Immutable.OrderedSet<string>, noCache?: boolean): void
Prefetch all items by keys.
Argument | Type | Description |
---|---|---|
keys | string[] | Immutable.List\<string> | Immutable.Set\<string> | Immutable.OrderedSet\<string> | Requested items keys. |
noCache | boolean | Update cached item from the server or other data source. |
public InvalidateCache(key: string): void
Removes item from cache, if it exists.
Argument | Type | Description |
---|---|---|
key | string | Requested item key. |
public InvalidateCacheMultiple(keys: string[]): void
Removes multiple items from cache, if they exist.
Argument | Type | Description |
---|---|---|
keys | string[] | Requested items key. |
public getInitialState(): Items<TValue>
TValue
- type of MapStore
item value.
Constructs the initial state for this store. This is called once during construction of the store.
Returns initial empty state.
public at(key: string): Item<TValue> | undefined
TValue
- type of MapStore
item value.
Access the value at the given key.
Returns undefined if the key does not exist in the cache.
Argument | Type | Description |
---|---|---|
key | string | Requested item key. |
protected RequestsIntervalInMs: number
With a large amount of requests MapStore
throttles them. This property defines interval between portions of requests.
Default value is 50
ms.
protected storeWillCleanUp: () => void
storeWillCleanUp
property holds a function that will be invoked before store clean up.
abstract class DataStore
import { DataStore } from "simplr-flux";
DataStore
extends ReduceStore
of SimplrFlux
. Documentation of ReduceStore
can be found here.
State of DataStore
is a Items
of any
value type. Check API section for Abstractions Items
.
constructor(dispatcher?: Flux.Dispatcher<DispatcherMessage<any>>)
Creates an instance of DataStore.
public getInitialState(): Items<any>
Constructs the initial state for this store. This is called once during construction of the store.
protected getValueFromState<TValue>(key: string, promiseFactory: () => Promise<TValue>, noCache: boolean = false): Item<TValue>
TValue
- type of specific DataStore
item value.
Returns specified item value. Starts resolving data with promiseFactory
.
Argument | Type | Description |
---|---|---|
key | string | Item key. |
promiseFactory | () => Promise\ | Function that returns promise of value to be resolved. |
noCache | boolean | Update cached item from the server or other data source. |
protected has(key: string): boolean
Checks if the cache has a particular key.
Argument | Type | Description |
---|---|---|
key | string | Item key. |
protected invalidateCache(key: string): void
Removes item from cache, if it exists.
Argument | Type | Description |
---|---|---|
key | string | Item key. |
protected invalidateCacheMultiple(keys: string[]): void
Remove multiple items from cache, if they exist.
Argument | Type | Description |
---|---|---|
keys | string[] | Items keys. |
References
License
Released under the AGPL-3.0.
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago