4 minutes read
The "hypereact" state library includes pre-defined managed reducers that enable developers to just focus on the actions design and implementation. The built-in reducers are ready-to-use and the general approach is based on:
The store manager, when initialized, accepts a configuration object that must match the "IReduxConfig" interface. Specifically the configuration object is used to define the reducer that will handle each state slices.
// redux configuration is based on slice keys as property names and reducer implementations as values
export interface IReduxConfig {
[slice: string]: IReducer<any>;
}
The reducer must be implemented as classes that honor the IReducer interface. Even if the "hypereact" state library provides developers with built-in reducers, a generic conventional reducer can be implemented as follows.
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0
};
// reducer as class that implements the IReducer<T> interface (with T equal to the state slice type)
class CounterReducer implements IReducer<CounterState> {
// traditional reduce function
reduce(state: CounterState = initialState, action: any): CounterState {
switch (action.type) {
case "COUNT_INCREMENT":
return { value: state.value + 1 };
case "COUNT_DECREMENT":
return { value: state.value - 1 };
default:
return state;
}
}
}
// redux configuration object that binds the counter state slice with the CounterReducer reducer class
const initialReduxConfig: IReduxConfig = {
"counter": new CounterReducer()
};
const storeManager = StoreManager.getInstance(initialReduxConfig);
// dispatch old-fashioned action object
storeManager.dispatch({
type: "COUNT_INCREMENT"
});
// dispatch old-fashioned action object
storeManager.dispatch({
type: "COUNT_DECREMENT"
});
The ready-to-be used reducers included in the library are:
All the built-in reducers are also available in the equivalent persistence-enabled implementation: PersistentReduceableReducer, PersistentMergeableReducer.
The ReduceableReducer supports "self-reduceable" action classes. The "self-reduceable" action classes must implement the IReduceableAction<T> interface that requires a reduce(state: T) function.
When using a ReduceableReducer class, developers can just proceed on:
It is important to notice that only the reduce function is allowed within the action class. The store manager ensures that the action is dispatched as a POJO and the reduce function is called with the this keyword referred to it.
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0
};
// reduceable action class decorated to set type and slice binding
@ReduxAction("COUNTER_INCREMENT", "counter")
export class CounterIncrement implements IReduceableAction<CounterState> {
// the reduce function is executed on action dispatch
// the state argument is set the a new state
// the this keyword refers to the action POJO representation)
reduce(state: CounterState) {
state.value++;
return state;
}
}
// reduceable action class without decorators
export class CounterDecrement implements IReduceableAction<CounterState> {
type: string = "COUNTER_DECREMENT";
slice: string = "counter";
reduce(state: CounterState) {
state.value--;
return state;
}
}
const initialReduxConfig: IReduxConfig = {
"counter": new ReduceableReducer(initialState)
};
const storeManager = StoreManager.getInstance(initialReduxConfig);
storeManager.dispatch(new CounterIncrement());
storeManager.dispatch(new CounterDecrement());
The MergeableReducer supports "patching" action classes. The "patching" action classes are just merged against the state.
When using a MergeableReducer class, developers are just required to define actions with same propertiescset to the values they want to change in the state slice.
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0
};
// reduceable action class decorated to set type and slice binding
@ReduxAction("COUNTER_INCREMENT", "counter")
export class CounterIncrement {
value: number;
// constructor is used for eventual current state retrieval
constructor() {
const currentState: CounterState = StoreManager.getInstance().getState(this.slice);
this.value = currentState.value + 1
}
}
// reduceable action class without decorators
export class CounterDecrement {
type: string = "COUNTER_DECREMENT";
slice: string = "counter";
value: number;
constructor() {
const currentState: CounterState = StoreManager.getInstance().getState(this.slice);
this.value = currentState.value + 1
}
}
const initialReduxConfig: IReduxConfig = {
"counter": new MergeableReducer(initialState)
};
const storeManager = StoreManager.getInstance(initialReduxConfig);
storeManager.dispatch(new CounterIncrement());
storeManager.dispatch(new CounterDecrement());