Hello there
I am currently working on minimizing repetitive code in my react application by utilizing Redux state. After choosing the Redux structure to use (refer to Context), I now aim to make it more concise. To achieve this, I have developed a generic actionCreator. However, I am facing difficulty in determining the desired generic syntax.
Generic Action Creator
Here is my approach towards creating a generic action generator:
export class genericAction<M, K> extends Action {
public reducer = (state: M) => ({ ...state, ...this.payload });
constructor(public readonly type: string | undefined, public payload: K) {
super();
}
}
Currently, only merging states is possible, but I am aiming for an optional parameter that includes a function. This function will enable custom state merges (e.g. state.count + payload). However, this is a separate topic of discussion.
Issue at Hand
The problem arises when I consider using { fubar: number}
instead of { loading: boolean }
. Clearly, 'fubar' does not belong in my ProductStateModel.
const someAction = new genericAction<ProductStateModel, { loading: boolean }>(ProductListActionTypes.REQUEST_START, { loading: true });
Inquiry
I wish to only utilize attributes from the ProductStateModel as the type for the generic K. Pseudo Code:
genericAction<M, K is type of M> extends Action
The overall goal is to generate the action with the following types and parameters:
- StateModel
- ProductListActionType
- Payload
Is this achievable? Or should I be considering a different solution altogether? I am open to modifying the StateModel if necessary. As someone new to redux, I strive to follow best practices. I attempted to implement Pick<T, K> in the generic method, but I am uncertain whether it aligns with my requirements or maybe I simply need some rest :D
Appreciate any guidance and assistance
Context
This marks my starting point
// State
export interface State {}
export interface ProductStateModel extends State {
products: Array<ProductDTO>;
loading: boolean;
error: string;
}
// Product Actions
export abstract class Action {
public readonly type: string | undefined;
protected constructor() {
return Object.assign({}, this);
}
abstract reducer(state: State): State;
}
// First of 3 Actions. They are all very similar. The generic function should replace all of them.
export class ProductListRequest extends Action {
public readonly type = ProductListActionTypes.REQUEST_START;
public reducer = (state: ProductStateModel) => ({ ...state, loading: this.payload.loading });
constructor(public payload: Pick<ProductStateModel, 'loading'>) {
super();
}
}
// Reducer
export const productListReducer = (state: ProductStateModel = defaultProductState, action: Action) => {
return isNil(action.reducer) ? action.reducer(state) : state;
};