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
JavaScript
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
})
});
}
});
}