Skip to content

Instantly share code, notes, and snippets.

@danmartens
Last active June 29, 2017 01:00
Show Gist options
  • Save danmartens/15fd3643635d88a0d87c72b2d6b036eb to your computer and use it in GitHub Desktop.
Save danmartens/15fd3643635d88a0d87c72b2d6b036eb to your computer and use it in GitHub Desktop.
Redux Operations
import type { CurrentUserState } from './types';
import createSession from './create-session';
import destroySession from './destroy-session';
import updatePassword from './update-password';
class CurrentUser extends OperationsModule {
static key = 'currentUser';
static initialState: CurrentUserState = {
email: null,
isAuthenticating: false
};
static operations = [
createSession,
destroySession,
updatePassword
];
createSession(email: string, password: string) {
return createSession.actionCreator({
email,
password
});
}
getAuthenicated(state: CurrentUserState) {
return state[this.constructor.key].email != null;
}
}
import type { CurrentUserState } from './types';
import createSession from './create-session';
import destroySession from './destroy-session';
import updatePassword from './update-password';
const currentUser = combineOperations(
createSession,
destroySession,
updatePassword
)({
email: null,
isAuthenticating: false
});
export const actionCreators = {
createSession: createSession.actionCreator,
destroySession: destroySession.actionCreator
};
export const selectors = {
getAuthenicated(state: CurrentUserState) {
return state[this.constructor.key].email != null;
}
};
// Use operations to group functionality that would normally be spread out into separate files.
// A module stucture might look like this:
//
// ├─ modules
// | └─ current-user
// | ├─ index.js
// | ├─ types.js
// | ├─ create-session.js
// | ├─ destroy-session.js
// | └─ update-password.js
import type { CurrentUserState } from './types';
type CreateSessionAction = {
type: 'USER/CreateSession'
};
class CreateSession extends Operation {
static actionType = 'USER/CREATE_SESSION';
actionCreator(email: string, password: string): CreateSessionAction {
return {
type: this.constructor.actionType,
status: null,
payload: { email, password }
};
}
reducer(action: CreateSessionAction, state: CurrentUserState): CurrentUserState {
switch (action.status) {
case 'pending': {
return {
...state,
isAuthenticating: true
};
}
}
}
*saga(action: CreateSessionAction) {
if (action.status === null) {
yield put({ type: this.constructor.actionType, status: 'pending' });
try {
const response = yield call(this.request, action.payload);
yield put({
type: this.constructor.actionType,
status: 'error',
payload: response
});
} catch (error) {
yield put({
type: this.constructor.actionType,
status: 'error',
payload: error
});
}
}
}
request({
email,
password
}: {
email: string,
password: string
}): Promise<User> {
return axios
.post('/api/sessions', {
user: {
email,
password
}
})
.then(({ data }) => data);
}
}
export default new CreateSession();
export type AsyncAction<
ActionType,
Request,
ResponseSuccess,
ResponseError = Error
> =
| {
type: ActionType,
status: null,
payload: Request
}
| {
type: ActionType,
status: 'pending',
payload: Request
}
| {
type: ActionType,
status: 'success',
payload: ResponseSuccess
}
| {
type: ActionType,
status: 'error',
payload: ResponseError
};
interface AsyncOperation<State, Action> {
createAction(payload: *): Action,
saga(action: Action): Generator<*, *, *>,
reducer(state: State, action: Action): State
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment