Skip to content

Instantly share code, notes, and snippets.

@Tauka
Created January 21, 2018 10:11
Show Gist options
  • Save Tauka/15d35f36771d2512200bd9dc9b074807 to your computer and use it in GitHub Desktop.
Save Tauka/15d35f36771d2512200bd9dc9b074807 to your computer and use it in GitHub Desktop.
How to structure redux for by-feature grouping
import { createActions, handleActions, combineActions } from "redux-actions";
import { call, put, takeLatest } from "redux-saga/effects";
import { api } from "../../helpers";
import { Root } from "../../redux";
/*
* TYPES
* --------------------------------------------------------------
*/
enum requestStatusEnum {
success,
resolved,
pending,
error
}
export interface User {
id: string | number;
type: string;
name: string;
}
export interface Auth {
readonly user: User | null;
readonly requestStatus: requestStatusEnum;
};
/*
* STATE
* --------------------------------------------------------------
*/
const authState: Auth = {
user: null,
requestStatus: requestStatusEnum.resolved
};
/*
* ACTION CREATORS
* --------------------------------------------------------------
*/
export type Authenticate = {
login: string;
password: string;
}
type AuthenticateRes = {
requestStatus: requestStatusEnum;
user: User;
}
const {
authenticate,
authenticateSuccess,
authenticateFailure
} = createActions({
AUTHENTICATE: (payload: Authenticate) => payload,
AUTHENTICATE_SUCCESS: (payload: AuthenticateRes) => payload as any,
AUTHENTICATE_FAILURE: (payload: AuthenticateRes) => payload as any
});
/*
* REDUCER
* --------------------------------------------------------------
*/
const reducer = handleActions(
{
[combineActions(authenticate)](state: Auth, action: ReduxActions.Action<Auth>) {
return { ...state, requestStatus: requestStatusEnum.pending };
},
[combineActions(authenticateSuccess, authenticateFailure)](
state: Auth,
action: ReduxActions.Action<AuthenticateRes>
) {
return { ...state, ...action.payload };
}
},
authState
);
/*
* SIDE EFFECTS
* --------------------------------------------------------------
*/
function* authAsync(action: ReduxActions.Action<{login: string, password: string}> ) {
const { login, password } = action!.payload!
try {
const authData = yield call(
api,
`http://localhost:9000/monitoring/api/login?login=${login}&password=${password}`
);
yield put(
authenticateSuccess({
requestStatus: requestStatusEnum.success,
user: authData
})
);
} catch (e) {
yield put(
authenticateFailure({
requestStatus: requestStatusEnum.error,
user: null
})
);
}
}
export function* saga() {
yield takeLatest(authenticate.toString(), authAsync);
}
/*
* SELECTORS
* --------------------------------------------------------------
*/
export const selectors = {
getUser: (state: Root) => state.auth.user,
getIsSuccess: (state: Root) =>
state.auth.requestStatus === requestStatusEnum.success,
getIsPending: (state: Root) =>
state.auth.requestStatus === requestStatusEnum.pending,
getIsError: (state: Root) => state.auth.requestStatus === requestStatusEnum.error
};
/*
* EXPORTS
* --------------------------------------------------------------
*/
export const actions = {
authenticate
};
export default reducer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment