Store by event scheme
The best way to use Foxstore
State
Event Scheme is a config there you pass for every event handlers by type.
First of all, let`s create a State. For example, we have some items which we want to display in a window by pages.
interface PagingModel {
pages: number;
currentPage: number;
total: number;
}
// Be careful, use type, but not interface
// Typescript behaviour :(
type State = {
items: Item[];
pageSize: number;
itemsOnDisplay: number;
paging?: PagingModel;
}
const InitState: State = {
items: [],
pageSize: 5,
itemsOnDisplay: 0,
paging: {
pages: 0,
currentPage: 0,
total: 0,
}
}
After that we can design our Event Scheme. Let`s begin from Event Scope - which Events do we need?
enum Events {
LoadItems = 'LoadItems',
ItemsLoaded = 'ItemsLoaded',
PageChanged = 'PageChanged',
}
It is not nessesary to use Enum, but it is good practice to keep strings in constants of enums to easily modify them. Now, prepare your handers.
Handlers
import { writeAs } from './helpers';
// Dispatch an Event contains Server Data
const loadItems: ActionFn<State, void> = () =>
new FoxEvent(Events.ItemsLoaded, someService.loadItems());
// Write our data from server to State by key 'items'
const writeItems: ReducerFn<State, Item[]> = writeAs('items');
// Update paging model when items are loaded
const setPagingModel: ReducerFn<State, Item[]> = (
items: Item[],
state: State,
): Partial<State> => ({
paging: {
pages: Math.round(items.length / state.pageSize),
currentPage: 1,
total: items.length,
},
})
// Update items for display when got they from server
const setDisplayingItems: ReducerFn<State, Item[]> = (
items: Item[],
state: State,
): Partial<State> => ({
itemsOnDisplay: (items || state.items).slice(
state.paging?.currentPage * state.pageSize - 1,
state.pageSize,
),
})
// Handle changing of page number
const changePage: ActionFn<State, number> = (page: number, state: State) =>
state.paging.pages >= page
? new FoxEvent(Events.PagingChanged, {
...state.paging,
currentPage: page,
})
: new FoxEvent('Error', 'Page number is not correct');
// Using another reducer to update data when page is changed
const updateDisplayItemsOnPageChange: ReducerFn<State, PagingModel> =
(paging: PagingModel, state: State) =>
setDisplayingItems(state.items, state)
Now we have a few small functions that are our bricks to build full system. Let`s put them on their places!
Event Scheme
const EventScheme = {
[Events.LoadItems]: createHandlers<State, void>({
actions: [
[loadItems],
],
})(Events.LoadItems),
[Events.ItemsLoaded]: createHandlers<State, Item[]>({
// You can order reducers to control changes
reducers: [
[writeItems],
[setPagingModel],
[setDisplayingItems],
],
})(Events.ItemsLoaded),
// I separated handling event from user and updating data
[Events.UpdatePage]: createHandlers<State, number>({
actions: [[changePage]]
})(Events.UpdatePage),
[Events.PagingChanged]: createHandlers<State, PagingModel>({
reducers: [
[writeAs<State>('paging')],
[updateDisplayItemsOnPageChange],
]
})(Events.PagingChanged)
}
type EventSchemeType = typeof EventScheme
So, you can build your scheme from small bricks, take parts of scheme and put it in another files, you can reuse them. That's why a function createHandlers
returns another function to get EventName. It is small overhead that allows us to create a pack of handlers and reuse it to a few events.
At the end, we need to create our store and use it.
import { DefaultStoreOptions } from './core/options';
const store = new ProtoStore<State, EventSchemeType>(
InitState,
EventScheme,
DefaultStoreOptions,
);
EventScheme gives us some features, for example - type checking of the payload when you are dispatching event


Last updated
Was this helpful?