Skip to content

how to mark controller's method as "blocking"?! #11

@plandem

Description

@plandem

Required:
somehow inside of controller without a lot of boilerplate code to get next: at begin of method dispatch action with promise for promise-middleware, that will be resolved/rejected at the end of method.

Recap:

  1. at begin of method set some redux state to true
  2. at end of method set same redux state to false
  3. at error do same as at (2)

In most cases it's common loading flag (e.g. API requests) to make UI to response changes of state (e.g. block/unblock controls).

Right now I'm doing it like this, but not sure that it's good way, maybe I miss something:

helper:

const blocking = (type, method, dispatch) => (...args)  => {
	return dispatch({ type, payload: new Promise((resolve, reject) => {
		try {
			resolve(method(...args));
		} catch (e) {
			reject(e);
		}
	})});
};

generators with marked onLogin as blocking:

const generators = {
	*initialize() {
		const { dispatch } = yield getProps;

		this.onLogin = blocking(types.APP_PREFIX, this.onLogin, dispatch);

		yield this.userInfo(true, '/');
	},

	*onSiderCollapse() {
		const { dispatch } = yield getProps;
		dispatch(actions.siderCollapse());
	},

	*onLogin(params) {
		const { dispatch } = yield getProps;

			try {
				yield api.login(params);
				yield this.userInfo(false, '/');
			} catch(e) {
				dispatch(error(e)),
				dispatch(auth.reset());
			}
	},

	*onLogout() {
		const { dispatch } = yield getProps;

		try {
			yield api.logout();
		} catch(e) {
		}

		dispatch(auth.reset());
		dispatch(navigate('/'));
	},

	*userInfo(silent = false, returnUrl) {
		const { dispatch, route } = yield getProps;

		try {
			dispatch(auth.request());
			const identity = yield api.fetchCurrrentUser();
			dispatch(auth.receive(identity));

			//if route has url to return, then use it
			let redirect = route && route.query && route.query.return;
			if(!redirect) {
				redirect = returnUrl;
			}

			if(redirect) {
				dispatch(navigate(redirect));
			}
		} catch(e) {
			console.log(e);

			if(!silent) {
				dispatch(error(e));
			}

			dispatch(auth.reset());
			dispatch(navigate('/'));
		}
	}
};

I need it for my reducer, e.g.:

import typeToReducer from 'type-to-reducer';
import * as loading from '../../utils/loading';
import types from './actions';

const initialState = {
	checking: false,
	siderCollapsed: false,
};

const app = typeToReducer({
	[types.SIDER_COLLAPSE]: (state, action) => ({ ...state, siderCollapsed: !state.siderCollapsed }),
	[types.APP_PREFIX]: loading.reducers('checking'),
}, initialState);

export default app;

utils is like this:

export const promiseTypeSuffixes = ['pending', 'fulfilled', 'rejected'];

export const reducers = (key, suffixes = promiseTypeSuffixes) => {
	const reducers = { };
	suffixes.forEach((suffix, i) => reducers[suffix] = (state, action) => ({ ...state, [key]: !i }));
	return reducers;
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions