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