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