UNPKG

npm-redux-interfaces

Version:
324 lines (277 loc) 10.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.RI = undefined; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _redux = require('redux'); 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; } var Store = function () { var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; // Set the initial store passed in from the user: var ClientStore = initial; /** * createReducerListener * * A method for creating a reducer listener and returning * an API to be used to add new listner hooks and to update * the existing ones with new state. */ var createReducerListener = function createReducerListener(interfaceName, reducerName) { var currentValueInReducer = null; /** * hooks * * An IIFE that is responsible for keeping track * of every listener hook. Listener hooks are * instantiated through the `.listen` command. */ var hooks = function () { var instances = []; return { getAll: function getAll() { return instances; }, add: function add(hook) { instances.push(hook); }, remove: function remove(position) { if (instances[position]) { instances[position] = null; } }, length: function length() { return instances.length; } }; }(); return _defineProperty({}, reducerName, { updateListeners: function updateListeners(newState) { var nextReducerState = newState[interfaceName][reducerName]; var dataTypeOfNextState = typeof nextReducerState === 'undefined' ? 'undefined' : _typeof(nextReducerState); var dataTypeOfCurrentState = typeof currentValueInReducer === 'undefined' ? 'undefined' : _typeof(currentValueInReducer); var dataHasChanged = dataTypeOfNextState !== dataTypeOfCurrentState || JSON.stringify(nextReducerState) !== JSON.stringify(currentValueInReducer); // Update every listener hook with the new value: hooks.getAll().forEach(function (listener) { if (listener) { if (listener.numberOfCalls && listener.currentCalls === listener.numberOfCalls) { listener.remove(); } else { if (listener.alwaysUpdate || dataHasChanged) { if (listener.actions && listener.actions.includes(newState._RI.lastAction.type)) { listener.callback(nextReducerState); listener.currentCalls = listener.currentCalls + 1; } } } } }); currentValueInReducer = nextReducerState; }, addListenerHook: function addListenerHook(callback) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var actions = arguments[2]; /** * addListenerHook * * The method is called when adding a new listener hook. * the `callback` argument is what gets called internally * to update the listener with the new state on change. */ var listenerHookPosition = hooks.length(); var listenerHook = { callback: callback, numberOfCalls: params.numberOfCalls || null, alwaysUpdate: params.alwaysUpdate || null, currentCalls: 0, actions: actions || null, remove: function remove() { hooks.remove(listenerHookPosition); } }; hooks.add(listenerHook); return { remove: listenerHook.remove }; } }); }; /** * reducerSubscriptions * * An IIFE that is responsible for instantiating and * keeping track of reducer subscriptions. A given reducer * can have only one subscription. Each subscription can * having multiple listener hooks. */ var reducerSubscriptions = function () { var subscriptions = {}; return { listen: function listen(interfaceName, reducerName, callback, params, actions) { /** * This function is executed in the context of a * new listener being added to a reducer subscription. */ var publicListenerHookMethods = null; if (!subscriptions.hasOwnProperty(interfaceName)) { // if the interface does not yet have any reducer // subscriptions, instantiate the first one: subscriptions[interfaceName] = createReducerListener(interfaceName, reducerName); publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions); } else if (subscriptions.hasOwnProperty(interfaceName) && !subscriptions[interfaceName][reducerName]) { // If the interface already has a subscription but // no listeners for this reducer, add the reducer // listener to the interface subscription: subscriptions[interfaceName] = Object.assign({}, subscriptions[interfaceName], createReducerListener(interfaceName, reducerName, callback)); publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions); } else { // if the interface already has a subscription and // the reducer already has a listener, add a new // listener hook: publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions); } return publicListenerHookMethods; }, updateSubscriptions: function updateSubscriptions(changesFromStore) { /** * An internal method for updating all reducer subscriptions * with the new state object. * * This is called internally on every state change in the store. */ for (var namespace in subscriptions) { var Interface = subscriptions[namespace]; for (var reducerName in Interface) { Interface[reducerName].updateListeners(changesFromStore); } } } }; }(); var subscribe = function subscribe() { /** * subscribe * * A method to be called on every update made to the * store. It is responsible for updating every reducer * subscription with the new state. * * @return {null} */ /** * handleChange * * A method for updating all of the subscriptions * with the new state from Redux: */ var handleChange = function handleChange() { var nextState = ClientStore.getState(); reducerSubscriptions.updateSubscriptions(nextState); }; // Subscribe to the store passed from the user // and save the disconnect method: var unsubscribeCallback = void 0; var createSubscriptionToStore = function createSubscriptionToStore(store) { // returns method to unsubscribe; unsubscribeCallback = store.subscribe(handleChange); }; return Object.assign({}, reducerSubscriptions, { createSubscriptionToStore: createSubscriptionToStore, unsubscribeCallback: unsubscribeCallback }); }; var StoreAPI = { subscribe: subscribe(), dispatch: function dispatch(action) { return ClientStore.dispatch(action); }, get: function get() { return ClientStore; }, set: function set(store) { ClientStore = store; StoreAPI.subscribe.createSubscriptionToStore(store); } }; return StoreAPI; }(); var Reducer = function Reducer(store, interfaceName, reducerName) { return { getState: function getState() { return eval('store.get().getState().' + interfaceName + '.' + reducerName); }, listen: function listen(callback, params, actions) { return store.subscribe.listen(interfaceName, reducerName, callback, params, actions); } }; }; var RootReducer = function () { var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var root_reducer = initial; var reducerStore = {}; return { addInterface: function addInterface(name, input) { // Format the inputs: var nextInterface = _defineProperty({}, name, input); // Add the new interface: reducerStore = Object.assign(reducerStore, nextInterface); // Rebuild the root_reducer: return root_reducer = (0, _redux.combineReducers)(reducerStore); }, get: function get() { return root_reducer; } }; }(); var mountInterface = function mountInterface(store, interfaceName, input) { // Check if the interface name does not conflict: if (!RI[interfaceName]) { // Build the actions: var actionsObj = {}; if (input.actions) { actionsObj = Object.keys(input.actions).reduce(function (obj, action) { obj[action] = function () { var _input$actions; return store.dispatch((_input$actions = input.actions)[action].apply(_input$actions, arguments)); }; return obj; }, {}); } // Build the reducers: var reducersObj = {}; if (input.reducers) { reducersObj = Object.keys(input.reducers).reduce(function (acc, reducerName) { acc[reducerName] = new Reducer(store, interfaceName, reducerName); return acc; }, {}); // Add the interface's reducers to the RootReducer: RootReducer.addInterface(interfaceName, (0, _redux.combineReducers)(input.reducers)); } // Mount the interface: return exports.RI = RI = Object.assign({}, RI, _defineProperty({}, interfaceName, Object.assign({}, actionsObj, reducersObj))); } // If the interface conflicts: console.error('\n Interface \'' + interfaceName + '\' is already in use. Try a different name for your interface.\n '); return err; }; var RI = exports.RI = { mount: function mount(namespace, input) { return mountInterface(Store, namespace, input); }, setStore: function setStore(input) { return Store.set(input); }, getStore: function getStore() { return Store.get(); }, getRootReducer: function getRootReducer() { return RootReducer.get(); } }; RI.mount('_RI', { reducers: { lastAction: function lastAction(state, action) { return action; } } });