@zedux/core
Version:
A high-level, declarative, composable form of Redux
61 lines (60 loc) • 2.5 kB
JavaScript
import { getMetaData, removeMeta } from '../api/meta.js';
import { zeduxTypes } from '../api/zeduxTypes.js';
import { BranchNodeType, StoreNodeType } from '../utils/general.js';
const getErrorMessage = (subStorePath) => `Zedux: store.dispatch() - Invalid Delegation - Store does not contain a child store at path: ${subStorePath.join(' -> ')}`;
const prodError = 'Minified Error';
/**
* Finds a node in a tree given a node path (array of nodes).
*/
const findChild = (tree, nodePath) => {
for (const node of nodePath) {
if (tree.type !== BranchNodeType) {
throw new ReferenceError(true /* DEV */ ? getErrorMessage(nodePath) : prodError);
}
tree = tree.children[node];
if (!tree) {
throw new ReferenceError(true /* DEV */ ? getErrorMessage(nodePath) : prodError);
}
}
return tree;
};
/**
* Delegates an action to a child store.
*
* Does nothing if the special `delegate` meta node is not present in the action
* chain.
*
* This expects the `metaData` of the `delegate` meta node to be an array
* containing a path of nodes describing the child store's location in the
* parent store's current hierarchy descriptor.
*
* Delegated actions will not be handled by the parent store at all.
*/
export const delegate = (tree, action) => {
const subStorePath = getMetaData(action, zeduxTypes.delegate);
if (!subStorePath || !tree)
return false;
const child = findChild(tree, subStorePath);
if (child.type !== StoreNodeType) {
throw new TypeError(true /* DEV */ ? getErrorMessage(subStorePath) : prodError);
}
;
child.store.dispatch(removeMeta(action, zeduxTypes.delegate));
};
/**
* Propagates a state change from a child store to a parent.
*
* Recursively finds the child store's node in the parent store's state tree and
* re-creates all the nodes down that path.
*
* #immutability
*/
export const propagateChange = (currentState, subStorePath, newSubStoreState, hierarchyConfig) => {
if (!subStorePath.length)
return newSubStoreState;
// at this point we can assume that currentState is a hierarhical structure
// these "currentState as any" casts should be fine
const newNode = hierarchyConfig.clone(currentState);
const nextNodeKey = subStorePath[0];
return hierarchyConfig.set(newNode, nextNodeKey, propagateChange(hierarchyConfig.get(currentState, nextNodeKey), subStorePath.slice(1), newSubStoreState, hierarchyConfig));
};