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 --saveConcepts
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:
Statusproperty for item loading status (checkAPIforItemStatus);Valuefor 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.OnFailurepublic 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.
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago