UNPKG

@zedux/core

Version:

A high-level, declarative, composable form of Redux

108 lines (107 loc) 4.16 kB
import { Store } from '../api/createStore.js'; import { detailedTypeof } from '../api/detailedTypeof.js'; import { is } from '../api/is.js'; import { isPlainObject } from '../api/isPlainObject.js'; import { zeduxTypes } from '../api/zeduxTypes.js'; import { BranchNodeType, NullNodeType, ReducerNodeType, StoreNodeType, } from '../utils/general.js'; /** * Converts a Branch hierarchy descriptor to a HierarchyNode's children * * Really should only be used from `hierarchyDescriptorToHierarchy()` */ const branchToHierarchyChildren = (branch, registerSubStore, currentPath) => { const children = {}; Object.entries(branch).forEach(([key, val]) => { const newPath = [...currentPath, key]; children[key] = hierarchyDescriptorToHierarchy(val, registerSubStore, newPath); }); return children; }; /** * Turns a non-branch node from a user-supplied hierarchy descriptor into a * HierarchyNode object */ const nonBranchToHierarchyNode = (type, hierarchy, registerSubStore, currentPath) => { if (type === NullNodeType) { return { type }; } if (type === ReducerNodeType) { return { type, reducer: hierarchy }; } // It's a Store hierarchy descriptor return { type: type, destroy: registerSubStore(currentPath, hierarchy), reducer: wrapStoreInReducer(hierarchy), store: hierarchy, }; }; /** * Determines the HierarchyNodeType of the given hierarchy descriptor. * * Throws a TypeError if the descriptor is invalid. */ export const getHierarchyType = (descriptor) => { if (typeof descriptor === 'function') return ReducerNodeType; if (descriptor && is(descriptor, Store)) return StoreNodeType; if (isPlainObject(descriptor)) return BranchNodeType; if (true /* DEV */ && descriptor != null) { throw new TypeError(`Zedux: store.use() - Hierarchy descriptor nodes must be reducers, stores, plain objects, or null. Received ${detailedTypeof(descriptor)}`); } return NullNodeType; }; /** * Turns a normal, user-supplied hierarchy descriptor into a Hierarchy for easy * reducer hierarchy creating, diffing, merging, and destroying. * * Also figures out the reducer for non-branch nodes. */ export const hierarchyDescriptorToHierarchy = (hierarchy, registerSubStore, currentPath = []) => { const type = getHierarchyType(hierarchy); if (type !== BranchNodeType) { return nonBranchToHierarchyNode(type, hierarchy, registerSubStore, currentPath); } // It's a Branch; recursively convert the whole tree. We don't need to supply // a reducer for this branch 'cause the merge process does that for us return { type, children: branchToHierarchyChildren(hierarchy, registerSubStore, currentPath), }; }; /** * Creates a reducer that wraps the entry points of the given store. * * This reducer will propagate actions down the child store's reducers. * * Wraps all actions in the special `inherit` meta node to inform the child * store's effects subscribers that this action was received from its parent * store. * * Since the parent store also registers an effects subscriber on this child * store, it will know not to propagate the inherited action from the child * store. UPDATE: Actually, it doesn't even need to check - the parent store * knows that it _isDispatching and can ignore child store actions while it is. */ export const wrapStoreInReducer = (store) => { const reducer = (state, action) => { // If this is the special hydrate or partial hydrate action, re-create the // action's payload using the current state slice if (action.type === zeduxTypes.hydrate || action.type === zeduxTypes.merge) { action = { type: zeduxTypes.hydrate, payload: state, }; } // Tell the child store's effect subscribers that this action is inherited const inheritedAction = { metaType: zeduxTypes.inherit, payload: action, }; return store.dispatch(inheritedAction); }; return reducer; };