redux-create
Version:
redux create reducer, action creator, async action creator, action type
239 lines (189 loc) • 6.64 kB
TypeScript
/**
* An *action* is a plain object that represents an intention to change the
* state. Actions are the only way to get data into the store. Any data,
* whether from UI events, network callbacks, or other sources such as
* WebSockets needs to eventually be dispatched as actions.
*
* Actions must have a `type` field that indicates the type of action being
* performed. Types can be defined as constants and imported from another
* module. It's better to use strings for `type` than Symbols because strings
* are serializable.
*
* Other than `type`, the structure of an action object is really up to you.
* If you're interested, check out Flux Standard Action for recommendations on
* how actions should be constructed.
*/
export interface Action<P> {
type: string;
payload?: P
}
/* reducers */
/**
* A *reducer* (also called a *reducing function*) is a function that accepts
* an accumulation and a value and returns a new accumulation. They are used
* to reduce a collection of values down to a single value
*
* Reducers are not unique to Redux—they are a fundamental concept in
* functional programming. Even most non-functional languages, like
* JavaScript, have a built-in API for reducing. In JavaScript, it's
* `Array.prototype.reduce()`.
*
* In Redux, the accumulated value is the state object, and the values being
* accumulated are actions. Reducers calculate a new state given the previous
* state and an action. They must be *pure functions*—functions that return
* the exact same output for given inputs. They should also be free of
* side-effects. This is what enables exciting features like hot reloading and
* time travel.
*
* Reducers are the most important concept in Redux.
*
* *Do not put API calls into reducers.*
*
* @template S State object type.
*/
export type Reducer<S> = <A extends Action<S>>(state: S, action: A) => S;
/* Create Action Type */
/**
* Create Action Type
* @param parts
*
* usage:
const PRODUCT = "PRODUCT";
const CATEGORY = "CATEGORY";
const SELECT = "SELECT";
cosnt CREATE = "CREATE";
const UPDATE = "UPDATE";
cosnt DELETE = "DELETE";
cosnt PRODUCT_SELECT = createActionType(PRODUCT, SELECT) // "PRODUCT_SELECT";
cosnt PRODUCT_CREATE = createActionType(PRODUCT, CREATE) // "PRODUCT_CREATE";
cosnt PRODUCT_UPDATE = createActionType(PRODUCT, UPDATE) // "PRODUCT_UPDATE";
cosnt PRODUCT_DELETE = createActionType(PRODUCT, DELETE) // "PRODUCT_DELETE";
*/
export function createActionType(...parts: [string]): string;
/* Create Action Creator */
/**
* A function that creates actions.
*/
export interface ActionCreator<P> {
(payload?: P): Action<P>;
}
/**
* Create an action creator
* @param type The ActionType
*
* usage:
* - javascript
const createProduct = createActionCreator(PRODUCT_CREATE)
const productData = {id: 1, name: "test"}
createProduct(productData) // {type: "PRODUCT_CREATE", payload: {id: 1, name: "test"}
* - Typescript
interface IProduct {
id: number;
name: string;
}
const createProduct = createActionCreator<IProduct>(PRODUCT_CREATE)
const productData: IProduct = {id: 1, name: "test"}
createProduct(productData) // {type: "PRODUCT_CREATE", payload: {id: 1, name: "test"}}
* - Flow Type
declare type TProduct = {
id: number;
name: string;
}
const createProduct = createActionCreator<TProduct>(PRODUCT_CREATE)
const productData: TProduct = {id: 1, name: "test"}
createProduct(productData) // {type: "PRODUCT_CREATE", payload: {id: 1, name: "test"}}
*/
export function createActionCreator<Payload>(...type: [string]): ActionCreator<Payload>;
/**
* Create an async action creator
* @param type
*
* usage:
* - javascript
export const fetchProductsAction = createAsyncActionCreator(FETCH_PRODUCTS);
export const fetchProducts = () => (dispatch) => {
dispatch(fetchProductsAction.request());
productsApi.fetchProducts().then(
(payload) => dispatch(fetchProductsAction.success(payload)),
(error) => dispatch(fetchProductsAction.failure(error)),
);
};
* - typescript
interface IErrorPayload {
message: string;
}
export const fetchProductsAction = createAsyncActionCreator<{}, IProduct[], IErrorPayload>(FETCH_PRODUCTS);
export const fetchProducts = () => (dispatch) => {
dispatch(fetchProductsAction.request());
productsApi.fetchProducts().then(
(payload) => dispatch(fetchProductsAction.success(payload)),
(error) => dispatch(fetchProductsAction.failure(error)),
);
};
* - Flow Type
declare type TErrorPayload = {
message: string;
}
export const fetchProductsAction = createAsyncActionCreator<{}, TProduct[], TErrorPayload>(FETCH_PRODUCTS);
export const fetchProducts = () => (dispatch) => {
dispatch(fetchProductsAction.request());
productsApi.fetchProducts().then(
(payload) => dispatch(fetchProductsAction.success(payload)),
(error) => dispatch(fetchProductsAction.failure(error)),
);
};
*/
export function createAsyncActionCreator<RequestPayload, SuccessPayload, FailurePayload>(...type: [string]): {
request: () => ActionCreator<RequestPayload>,
success: () => ActionCreator<SuccessPayload>,
failure: () => ActionCreator<FailurePayload>,
}
/* Create Reducer */
export interface CustomReducersMapObject {
[key: string]: CustomReducersMapObject | void;
}
/**
* Create an reducer
* @param defaultState The default state of the reducer
* @param reducerMap The tree object containing 'Leafs' with reducer functions
*
* usage:
* - javascript
const selectedProduct = createReducer(state = null, {
[PRODUCT]: {
[SELECT]: (state, payload) => payload,
[TOGGLE]: (state, payload) => state === payload ? null : payload,
[UNSELECT]: () => null,
}
});
* - typescript
const selectedProduct = createReducer(state: null | number = null, {
[PRODUCT]: {
[SELECT]: (state, payload) => payload,
[TOGGLE]: (state, payload) => state === payload ? null : payload,
[UNSELECT]: () => null,
}
});
* - Flow Type
const selectedProduct = createReducer(state: null | number = null, {
[PRODUCT]: {
[SELECT]: (state, payload) => payload,
[TOGGLE]: (state, payload) => state === payload ? null : payload,
[UNSELECT]: () => null,
}
});
* - same code written using switch
const selectedProduct = (state = null, action) => {
switch (action.type) {
case PRODUCT_SELECT:
return action.payload;
case PRODUCT_TOGGLE:
return state === action.payload ? null : action.payload;
case PRODUCT_UNSELECT:
return null;
default:
return state;
}
};
*/
export function createReducer<S, CustomReducersMapObject>(defaultState, reducerMap): Reducer<S>;