135 lines
3.5 KiB
JavaScript
135 lines
3.5 KiB
JavaScript
|
|
import createDebug from 'debug';
|
|
const debug = createDebug('d3d:util:asyncActions');
|
|
|
|
const defaultSuffixes = ['PENDING', 'FULFILLED', 'REJECTED'];
|
|
|
|
/*
|
|
* Some utils below are quite similar to the following util:
|
|
* https://github.com/rjbma/redux-promise-reducer
|
|
*/
|
|
|
|
export function createPromiseActionCreators(
|
|
instance,
|
|
fnNames,
|
|
promiseWrapper,
|
|
actionPrefix = '',
|
|
suffixes = defaultSuffixes
|
|
) {
|
|
let actionCreators = {};
|
|
for (const fnName of fnNames) {
|
|
actionCreators = {
|
|
...actionCreators,
|
|
...createPromiseActionCreator(instance, fnName, promiseWrapper, actionPrefix, suffixes)
|
|
};
|
|
}
|
|
return actionCreators;
|
|
}
|
|
|
|
/**
|
|
* Creates an actionCreator that returns an
|
|
* redux-promise-middleware compatible action with:
|
|
* - type based on (prefix+) function name and
|
|
* - the promise the function returns
|
|
*/
|
|
export function createPromiseActionCreator(
|
|
instance,
|
|
fnName,
|
|
promiseWrapper,
|
|
actionPrefix = '',
|
|
suffixes = defaultSuffixes
|
|
) {
|
|
const fn = instance[fnName];
|
|
if (fn === undefined) {
|
|
throw new Error(`Given instance doesn't have a function named '${fnName}'`);
|
|
}
|
|
actionPrefix = actionPrefix ? `${actionPrefix}_` : '';
|
|
const fnNameUpper = fnName.toUpperCase();
|
|
|
|
const pendingType = actionPrefix + fnNameUpper + '_' + suffixes[0];
|
|
const fulfilledType = actionPrefix + fnNameUpper + '_' + suffixes[1];
|
|
const rejectedType = actionPrefix + fnNameUpper + '_' + suffixes[2];
|
|
|
|
return {
|
|
[fnName]: function actionCreator() {
|
|
debug('actionCreator called: ', fnName, arguments);
|
|
const promise = fn.apply(instance, arguments);
|
|
return {
|
|
type: actionPrefix + fnNameUpper,
|
|
payload: {
|
|
promise: promiseWrapper ? promiseWrapper(promise, fnName, arguments) : promise,
|
|
data: arguments // send as payload with pending action
|
|
},
|
|
// include arguments to meta so it's also included in
|
|
// the fulfilled and rejected action types
|
|
meta: {
|
|
args: arguments
|
|
}
|
|
};
|
|
},
|
|
[pendingType]: pendingType,
|
|
[fulfilledType]: fulfilledType,
|
|
[rejectedType]: rejectedType
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a reducer that manages state for async actions
|
|
*/
|
|
export function createAsyncActionsReducer(pendingType, fulfilledType, rejectedType) {
|
|
return function reducer(
|
|
state = {
|
|
pending: false,
|
|
error: null,
|
|
data: null
|
|
},
|
|
action
|
|
) {
|
|
switch (action.type) {
|
|
case pendingType:
|
|
return {
|
|
pending: true,
|
|
data: null,
|
|
error: null
|
|
};
|
|
case fulfilledType:
|
|
return {
|
|
pending: false,
|
|
data: action.payload,
|
|
error: null
|
|
};
|
|
case rejectedType:
|
|
return {
|
|
pending: false,
|
|
data: null,
|
|
error: action.payload
|
|
};
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* creates a reducers for a list of names
|
|
* result is usable for combineReducers()
|
|
*/
|
|
export function createAsyncActionsReducers(
|
|
prefix,
|
|
names,
|
|
// default suffixes from redux-promise-middleware:
|
|
suffixes = defaultSuffixes
|
|
) {
|
|
const reducers = {};
|
|
prefix = prefix ? `${prefix}_` : '';
|
|
|
|
for (const name of names) {
|
|
const nameUpper = name.toUpperCase();
|
|
const pendingType = prefix + nameUpper + '_' + suffixes[0];
|
|
const fulfilledType = prefix + nameUpper + '_' + suffixes[1];
|
|
const rejectedType = prefix + nameUpper + '_' + suffixes[2];
|
|
reducers[name] = createAsyncActionsReducer(pendingType, fulfilledType, rejectedType);
|
|
}
|
|
return reducers;
|
|
}
|