-
Notifications
You must be signed in to change notification settings - Fork 2
Frontend Actions
There are two kinds of Redux actions: plain actions and thunk actions.
Plain actions are objects with a type
field and a payload
field.
type PlainAction = {
type: string,
payload: any,
};
An ordinary plain action creator will be like:
const SET_BASE_LEVEL = 'SET_BASE_LEVEL';
const setBaseLevel =
(baseLevel: ZoomLevel) => ({
type: SET_BASE_LEVEL as typeof SET_BASE_LEVEL,
payload: { baseLevel },
});
The setBaseLevel
function is a plain action creator (or a plain action data constructor if you treat an action a type) and it returns a plain action.
emptyAction
is an action with a null type. It's the fallback action of many reducers, so those reducers won't have to check if the type
field exists or not.
export const emptyAction = { type: null };
The ActionUnion
type is a type level function to extract return types of a map object full of action creators. So we can just create our action types and action creator functions instead of type the action first.
type FunctionType = (...args: any[]) => any;
type ActionCreatorsMap = { [actionCreator: string]: FunctionType };
export type ActionUnion<A extends ActionCreatorsMap> = typeof emptyAction | ReturnType<A[keyof A]>;
Thunk actions are async functions with the ability to access current application states and dispatch other actions. A typical thunk action may look like this:
const panToObject =
(oid: ObjectID) =>
async (dispatch: Dispatch, getState: GetState) => {
const { senseObject, viewport: v } = getState();
const { x = 0, y = 0 } = CS.getObject(senseObject, oid);
const pt = { x: v.width / 2 - x, y: v.height / 2 - y };
dispatch(panViewport(pt));
};
If you view thunk actions as a generic type:
type ThunkAction<T> = (dispatch: Dispatch, getState: GetState) => Promise<T>;
Then the type of thunk action creators is:
type ThunkActionCreator<T> = (...args: any[]) => ThunkAction<T>;
Thunk actions may handle errors if they have to use those errors to make decisions. You should handle other errors in your UI event handlers so you may not crash the whole application.
A thunk action dispatch many thunk actions or plain actions. It makes the thunk action a effective action stream for Redux architecture. With the power of async/await
, you can compose actions by hand or create some helper functions for them.
export const submitNewPassword =
() =>
async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const { session: { user }, settings } = state;
const oldPassword = getOldPassword(settings);
const newPassword = getNewPassword(settings);
const r0 = await U.verifyPassword(user, oldPassword);
if (!r0) {
return dispatch(updatePasswordStatus(PasswordStatus.OLD_PASSWORD_WRONG));
}
const r1 = await U.updatePassword(user, newPassword);
if (!r1) {
return dispatch(updatePasswordStatus(PasswordStatus.NEW_PASSWORD_INVALID));
}
return dispatch(updatePasswordStatus(PasswordStatus.SUCCESS));
};
The mapDispatch
function in src/types/map-dispatch.ts
is a tool to apply the Redux dispatch function to many actions. It's not well typed and we need help to type it properly. We use it in almost any connected UI component.