UNPKG

redux-source

Version:

Using GraphQL schema and query language to access any data source (eg. RESTful APIs) and automatically generate reducers, actions and normalized state

202 lines (169 loc) 5.76 kB
import "core-js/modules/web.dom.iterable"; import _difference from "lodash/difference"; import _union from "lodash/union"; function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import { createSource as _createSource } from 'redux-source-utils'; const combineMethods = { replace({ prevResult, prevEntities, result, entities }) { const newState = {}; const newResults = _objectSpread({}, prevResult); for (const resultName in result) { newResults[resultName] = result[resultName]; } const newEntities = {}; for (const entityName in entities) { newEntities[entityName] = _objectSpread({}, prevEntities[entityName] || {}); for (const entityId in entities[entityName]) { newEntities[entityName][entityId] = _objectSpread({}, (prevEntities[entityName] || {})[entityId] || {}, entities[entityName][entityId]); } } if (Object.keys(result).length) { newState.result = _objectSpread({}, prevResult, newResults); } if (Object.keys(newEntities).length) { newState.entities = _objectSpread({}, prevEntities, newEntities); } return newState; }, merge({ prevResult, prevEntities, result, entities }) { const newState = {}; const newResults = _objectSpread({}, prevResult); for (const resultName in result) { if (Array.isArray(result[resultName]) && (result[resultName].length === 0 || typeof result[resultName][0] !== 'object')) { newResults[resultName] = _union(prevResult[resultName], result[resultName]); } else { newResults[resultName] = result[resultName]; } } const newEntities = {}; for (const entityName in entities) { newEntities[entityName] = _objectSpread({}, prevEntities[entityName] || {}); for (const entityId in entities[entityName]) { newEntities[entityName][entityId] = _objectSpread({}, (prevEntities[entityName] || {})[entityId] || {}, entities[entityName][entityId]); } } if (Object.keys(result).length) { newState.result = _objectSpread({}, prevResult, newResults); } if (Object.keys(newEntities).length) { newState.entities = _objectSpread({}, prevEntities, newEntities); } return newState; }, crop({ prevResult, result }) { const newState = {}; const newResult = _objectSpread({}, prevResult); let isResultChange = false; for (const resultName in result) { if (Array.isArray(result[resultName]) && (result[resultName].length === 0 || typeof result[resultName][0] !== 'object')) { const filterd = _difference(prevResult[resultName], result[resultName]); if (filterd.length !== prevResult[resultName].length) { newResult[resultName] = filterd; isResultChange = true; } } else if (prevResult[resultName] === result[resultName]) { delete newResult[resultName]; isResultChange = true; } } if (isResultChange) { newState.result = newResult; } return newState; } }; export function createSource({ schema: typeDefs, resolvers }) { return _createSource({ typeDefs, resolvers, createInitialState: ({ stateName }) => ({ [stateName]: { result: {}, entities: {}, isPending: false, pendingCount: 0, inited: false, errors: [] } }), pendingReducer: ({ stateName }) => state => { const newSourceState = { isPending: true, pendingCount: state[stateName].pendingCount + 1 }; if (state[stateName].errors.length) { newSourceState.errors = []; } return _objectSpread({}, state, { [stateName]: _objectSpread({}, state[stateName], newSourceState) }); }, successReducer: ({ stateName, config }) => (state, { payload = {} }) => { const { result: prevResult, entities: prevEntities, pendingCount } = state[stateName]; const combineMethod = combineMethods[config.combineResult || 'merge']; const newNormalizedData = combineMethod({ prevResult, prevEntities, result: payload.result || {}, entities: payload.entities || {} }) || {}; const count = pendingCount - 1; const newSourceState = _objectSpread({}, newNormalizedData, { isPending: count > 0, pendingCount: count, inited: true }); if (state[stateName].errors.length) { newSourceState.errors = []; } return _objectSpread({}, state, { [stateName]: _objectSpread({}, state[stateName], newSourceState) }); }, errorReducer: ({ stateName }) => (state, { payload }) => { const count = state[stateName].pendingCount - 1; return _objectSpread({}, state, { [stateName]: _objectSpread({}, state[stateName], { isPending: count > 0, pendingCount: count, inited: true, errors: payload }) }); } }); }