Skip to content

Commit

Permalink
Data: Support subscribers per reducer key(s)
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Jan 3, 2019
1 parent b4ff7cf commit 32068e8
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/data/src/namespace-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function createNamespace( key, options, registry ) {
lastState = state;

if ( hasChanged ) {
listener();
listener( key );
}
} );
};
Expand Down
74 changes: 65 additions & 9 deletions packages/data/src/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import {
castArray,
without,
mapValues,
} from 'lodash';
Expand All @@ -12,6 +13,16 @@ import {
import createNamespace from './namespace-store.js';
import dataStore from './store';

/**
* Given an array of functions, invokes each function in the array with an
* empty argument set.
*
* @param {Function[]} fns Functions to invoke.
*/
function invokeForEach( fns ) {
fns.forEach( ( fn ) => fn() );
}

/**
* An isolated orchestrator of store registrations.
*
Expand Down Expand Up @@ -40,27 +51,72 @@ import dataStore from './store';
*/
export function createRegistry( storeConfigs = {} ) {
const stores = {};
let listeners = [];
let globalListeners = [];
const listenersByKey = {};

/**
* Global listener called for each store's update.
*
* @param {?string} reducerKey Key of reducer which changed, if provided by
* the registry implementation.
*/
function globalListener() {
listeners.forEach( ( listener ) => listener() );
function onStoreChange( reducerKey ) {
invokeForEach( globalListeners );

if ( reducerKey ) {
if ( listenersByKey[ reducerKey ] ) {
invokeForEach( listenersByKey[ reducerKey ] );
}
} else {
// For backwards compatibility with non-namespace-store, reducerKey
// is optional. If omitted, call every listenersByKey.
for ( const [ , listeners ] of Object.entries( listenersByKey ) ) {
invokeForEach( listeners );
}
}
}

/**
* Subscribe to changes to any data.
*
* @param {Function} listener Listener function.
* @param {Function} listener Listener function.
* @param {?(string|Array<string>)} reducerKeys Optional subset of reducer
* keys on which subscribe
* function should be called.
*
* @return {Function} Unsubscribe function.
* @return {Function} Unsubscribe function.
*/
const subscribe = ( listener ) => {
listeners.push( listener );
const subscribe = ( listener, reducerKeys ) => {
if ( reducerKeys ) {
// Overload to support string argument of `reducerKeys`.
reducerKeys = castArray( reducerKeys );

reducerKeys.forEach( ( reducerKey ) => {
if ( ! listenersByKey[ reducerKey ] ) {
listenersByKey[ reducerKey ] = [];
}

listenersByKey[ reducerKey ].push( listener );
} );

return () => {
reducerKeys.forEach( ( reducerKey ) => {
listenersByKey[ reducerKey ] = without(
listenersByKey[ reducerKey ],
listener
);

if ( ! listenersByKey[ reducerKey ].length ) {
delete listenersByKey[ reducerKey ];
}
} );
};
}

globalListeners.push( listener );

return () => {
listeners = without( listeners, listener );
globalListeners = without( globalListeners, listener );
};
};

Expand Down Expand Up @@ -122,7 +178,7 @@ export function createRegistry( storeConfigs = {} ) {
throw new TypeError( 'config.subscribe must be a function' );
}
stores[ key ] = config;
config.subscribe( globalListener );
config.subscribe( onStoreChange );
}

let registry = {
Expand Down

0 comments on commit 32068e8

Please sign in to comment.