@snipsonian/observable-state
Version:
Observable-state snippets (redux-like)
163 lines (162 loc) • 8.33 kB
JavaScript
import isSet from '@snipsonian/core/es/is/isSet';
import { createObservableStateAction } from '../actionCreators';
import { AsyncOperation, } from './types';
import { ASYNC_OPERATION_2_ASYNC_ENTITY_UPDATERS } from './asyncEntityUpdaters';
export function initAsyncEntityActionCreators({ entitiesStateField = 'entities', getEntitiesInitialState, }) {
const asyncEntityActionCreators = {
getAsyncEntity: (state, asyncEntityKey) => state[entitiesStateField][asyncEntityKey],
updateAsyncEntityInState: ({ asyncEntityKey, entityUpdater, options, setState, }) => {
setState({
newState: (currentState) => {
const entity = asyncEntityActionCreators.getAsyncEntity(currentState, asyncEntityKey);
return Object.assign(Object.assign({}, currentState), { [entitiesStateField]: Object.assign(Object.assign({}, currentState[entitiesStateField]), { [asyncEntityKey]: entityUpdater(entity) }) });
},
notificationsToTrigger: options.notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger: options.nrOfParentNotificationLevelsToTrigger,
});
},
fetchAsyncEntityAction: ({ asyncEntityKey, api, apiInputSelector, mapApiResponse, notificationsToTrigger, nrOfParentNotificationLevelsToTrigger, resetDataOnTrigger = true, onTrigger, onPreSuccess, onSuccess, onError, }) => createAsyncEntityActionBase({
asyncEntityKey,
api,
apiInputSelector,
mapApiResponse,
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
operation: AsyncOperation.fetch,
resetDataOnTrigger,
updateDataOnSuccess: true,
onTrigger,
onPreSuccess,
onSuccess,
onError,
}),
createAsyncEntityAction: ({ asyncEntityKey, api, apiInputSelector, mapApiResponse, notificationsToTrigger, nrOfParentNotificationLevelsToTrigger, updateDataOnSuccess = false, markAsFetchedOnSuccess, onTrigger, onPreSuccess, onSuccess, onError, }) => createAsyncEntityActionBase({
asyncEntityKey,
api,
apiInputSelector,
mapApiResponse,
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
operation: AsyncOperation.create,
resetDataOnTrigger: false,
updateDataOnSuccess,
markAsFetchedOnSuccess: isSet(markAsFetchedOnSuccess) ? markAsFetchedOnSuccess : updateDataOnSuccess,
onTrigger,
onPreSuccess,
onSuccess,
onError,
}),
updateAsyncEntityAction: ({ asyncEntityKey, api, apiInputSelector, mapApiResponse, notificationsToTrigger, nrOfParentNotificationLevelsToTrigger, updateDataOnSuccess = false, onTrigger, onPreSuccess, onSuccess, onError, }) => createAsyncEntityActionBase({
asyncEntityKey,
api,
apiInputSelector,
mapApiResponse,
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
operation: AsyncOperation.update,
resetDataOnTrigger: false,
updateDataOnSuccess,
onTrigger,
onPreSuccess,
onSuccess,
onError,
}),
removeAsyncEntityAction: ({ asyncEntityKey, api, apiInputSelector, notificationsToTrigger, nrOfParentNotificationLevelsToTrigger, markAsNotFetchedOnSuccess = true, onTrigger, onPreSuccess, onSuccess, onError, }) => createAsyncEntityActionBase({
asyncEntityKey,
api,
apiInputSelector,
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
operation: AsyncOperation.remove,
resetDataOnTrigger: false,
updateDataOnSuccess: false,
markAsNotFetchedOnSuccess,
onTrigger,
onPreSuccess,
onSuccess,
onError,
}),
};
return asyncEntityActionCreators;
function createAsyncEntityActionBase({ asyncEntityKey, api, apiInputSelector, mapApiResponse, notificationsToTrigger, nrOfParentNotificationLevelsToTrigger, operation, resetDataOnTrigger, updateDataOnSuccess, markAsFetchedOnSuccess = false, markAsNotFetchedOnSuccess = false, onTrigger, onPreSuccess, onSuccess, onError, }) {
return createObservableStateAction({
type: `${asyncEntityKey}_${operation.toUpperCase()}`,
payload: {
operation,
},
async process({ getState, setState, dispatch }) {
const asyncEntityUpdaters = ASYNC_OPERATION_2_ASYNC_ENTITY_UPDATERS[operation];
const asyncEntityUpdatersFetch = ASYNC_OPERATION_2_ASYNC_ENTITY_UPDATERS[AsyncOperation.fetch];
try {
asyncEntityActionCreators.updateAsyncEntityInState({
entityUpdater: (entity) => {
return resetDataOnTrigger
? asyncEntityUpdaters.trigger(entity, getEntitiesInitialState()[asyncEntityKey].data)
: asyncEntityUpdaters.triggerWithoutDataReset(entity);
},
asyncEntityKey,
setState,
options: {
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
},
});
if (onTrigger) {
onTrigger({ state: getState(), dispatch });
}
const apiInput = isSet(apiInputSelector)
? apiInputSelector({ state: getState() })
: {};
const apiResponse = await api(apiInput);
const apiResult = isSet(mapApiResponse)
? mapApiResponse({ response: apiResponse, state: getState() })
: apiResponse;
if (onPreSuccess) {
onPreSuccess({ apiResponse, apiResult, apiInput, state: getState(), dispatch });
}
asyncEntityActionCreators.updateAsyncEntityInState({
entityUpdater: (entity) => {
const updatedEntity = updateDataOnSuccess
? asyncEntityUpdaters.succeeded(entity, apiResult)
: asyncEntityUpdaters.succeededWithoutDataSet(entity);
if (markAsFetchedOnSuccess) {
return asyncEntityUpdatersFetch.succeededWithoutDataSet(updatedEntity);
}
if (markAsNotFetchedOnSuccess) {
return asyncEntityUpdatersFetch.resetWithoutDataReset(updatedEntity);
}
return updatedEntity;
},
asyncEntityKey,
setState,
options: {
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
},
});
if (onSuccess) {
onSuccess({ apiResponse, apiResult, apiInput, state: getState(), dispatch });
}
}
catch (error) {
asyncEntityActionCreators.updateAsyncEntityInState({
entityUpdater: (entity) => asyncEntityUpdaters.failed(entity, error),
asyncEntityKey,
setState,
options: {
notificationsToTrigger,
nrOfParentNotificationLevelsToTrigger,
},
});
if (onError) {
onError({
error: error,
state: getState(),
dispatch,
});
}
}
},
});
}
}