From fc906f100f87117fa98446e9d1368301750c74d8 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Tue, 2 Oct 2018 11:47:17 -0400 Subject: [PATCH 01/14] data: Start split of registry with reducer This splits out the implementation of `registerReducer` into a separate file, with the intention of continuing the rest of the store implementation. This will make room for other possible implementations with a common interface. --- packages/data/src/namespace-store.js | 37 ++++++++++++++++++++++++++++ packages/data/src/registry.js | 23 ++++------------- 2 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 packages/data/src/namespace-store.js diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js new file mode 100644 index 0000000000000..6a3df559074c8 --- /dev/null +++ b/packages/data/src/namespace-store.js @@ -0,0 +1,37 @@ +/** + * External dependencies + */ +import { createStore as createReduxStore } from 'redux'; +import { + flowRight, +} from 'lodash'; + +/** + * Creates a namespace object with a store derived from the reducer given. + * + * @param {Object} reducer Reducer function. + * @param {Array} enhancers Array of enhancer functions to be added to the store. + * @param {function} globalListener TODO: Remove this after subscribe is passed correctly. + * + * @return {Object} Store Object. + */ +export function createNamespace( reducer, enhancers, globalListener ) { + const store = createReduxStore( reducer, flowRight( enhancers ) ); + const namespace = { store, reducer }; + + // TODO: Move this to a subscribe function instead of referencing globalListener. + // Customize subscribe behavior to call listeners only on effective change, + // not on every dispatch. + let lastState = store.getState(); + store.subscribe( () => { + const state = store.getState(); + const hasChanged = state !== lastState; + lastState = state; + + if ( hasChanged ) { + globalListener(); + } + } ); + + return namespace; +} diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index b647934bfa821..d5d0b32821b74 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -1,17 +1,17 @@ /** * External dependencies */ -import { createStore, applyMiddleware } from 'redux'; import { - flowRight, without, mapValues, get, } from 'lodash'; +import { applyMiddleware } from 'redux'; /** * Internal dependencies */ +import { createNamespace } from './namespace-store.js'; import dataStore from './store'; import promise from './promise-middleware'; import createResolversCacheMiddleware from './resolvers-cache-middleware'; @@ -73,23 +73,10 @@ export function createRegistry( storeConfigs = {} ) { if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) { enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: reducerKey, instanceId: reducerKey } ) ); } - const store = createStore( reducer, flowRight( enhancers ) ); - namespaces[ reducerKey ] = { store, reducer }; - - // Customize subscribe behavior to call listeners only on effective change, - // not on every dispatch. - let lastState = store.getState(); - store.subscribe( () => { - const state = store.getState(); - const hasChanged = state !== lastState; - lastState = state; - - if ( hasChanged ) { - globalListener(); - } - } ); - return store; + // TODO: Remove globalListener from this call after subscriptions are passed. + namespaces[ reducerKey ] = createNamespace( reducer, enhancers, globalListener ); + return namespaces[ reducerKey ].store; } /** From 5e88f30a49af19c5b0e8e082a13e0ae2e8083db5 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Tue, 2 Oct 2018 12:51:34 -0400 Subject: [PATCH 02/14] data: Move registerSelectors and registerActions This moves the implementation of registerSelectors and registerActions to the namespace-store implementation file. --- packages/data/src/namespace-store.js | 27 +++++++++++++++++++++++++++ packages/data/src/registry.js | 14 +++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index 6a3df559074c8..50af2b5e86622 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -4,6 +4,7 @@ import { createStore as createReduxStore } from 'redux'; import { flowRight, + mapValues, } from 'lodash'; /** @@ -35,3 +36,29 @@ export function createNamespace( reducer, enhancers, globalListener ) { return namespace; } + +/** + * Sets selectors for given namespace. + * + * @param {Object} namespace The namespace object to modify. + * @param {Object} selectors Selectors to register. Keys will be used as the + * public facing API. Selectors will get passed the + * state as first argument. + */ +export function setSelectors( namespace, selectors ) { + const { store } = namespace; + const createStateSelector = ( selector ) => ( ...args ) => selector( store.getState(), ...args ); + namespace.selectors = mapValues( selectors, createStateSelector ); +} + +/** + * Sets actions for given namespace. + * + * @param {Object} namespace The namespace object to modify. + * @param {Object} actions Actions to register. + */ +export function setActions( namespace, actions ) { + const { store } = namespace; + const createBoundAction = ( action ) => ( ...args ) => store.dispatch( action( ...args ) ); + namespace.actions = mapValues( actions, createBoundAction ); +} diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index d5d0b32821b74..fe0a63318b913 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -11,7 +11,11 @@ import { applyMiddleware } from 'redux'; /** * Internal dependencies */ -import { createNamespace } from './namespace-store.js'; +import { + createNamespace, + setActions, + setSelectors, +} from './namespace-store.js'; import dataStore from './store'; import promise from './promise-middleware'; import createResolversCacheMiddleware from './resolvers-cache-middleware'; @@ -89,9 +93,7 @@ export function createRegistry( storeConfigs = {} ) { * state as first argument. */ function registerSelectors( reducerKey, newSelectors ) { - const store = namespaces[ reducerKey ].store; - const createStateSelector = ( selector ) => ( ...args ) => selector( store.getState(), ...args ); - namespaces[ reducerKey ].selectors = mapValues( newSelectors, createStateSelector ); + setSelectors( namespaces[ reducerKey ], newSelectors ); } /** @@ -147,9 +149,7 @@ export function createRegistry( storeConfigs = {} ) { * @param {Object} newActions Actions to register. */ function registerActions( reducerKey, newActions ) { - const store = namespaces[ reducerKey ].store; - const createBoundAction = ( action ) => ( ...args ) => store.dispatch( action( ...args ) ); - namespaces[ reducerKey ].actions = mapValues( newActions, createBoundAction ); + setActions( namespaces[ reducerKey ], newActions ); } /** From 534e09a9592bb11417daa4b8b755e1c2240e9ae9 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Thu, 4 Oct 2018 18:25:42 -0400 Subject: [PATCH 03/14] data: Untangle registerResolvers This untangles registerResolvers, puts more code into the namespace-store, and organizes the functions upon which it depends. --- packages/data/src/namespace-store.js | 45 ++++++ packages/data/src/registry.js | 234 ++++++++++++--------------- 2 files changed, 150 insertions(+), 129 deletions(-) diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index 50af2b5e86622..bb57de4633838 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -62,3 +62,48 @@ export function setActions( namespace, actions ) { const createBoundAction = ( action ) => ( ...args ) => store.dispatch( action( ...args ) ); namespace.actions = mapValues( actions, createBoundAction ); } + +/** + * Sets resolvers for a given namespace. Resolvers are side effects + * invoked once per argument set of a given selector call, used in ensuring + * that the data needs for the selector are satisfied. + * + * @param {Object} namespace The namespace object to modify. + * @param {Object} resolvers Resolvers to register. + * @param {Object} fulfillment Fulfillment implementation functions. + */ +export function setResolvers( namespace, resolvers, fulfillment ) { + namespace.resolvers = mapValues( resolvers, ( resolver ) => { + const { fulfill: resolverFulfill = resolver } = resolver; + return { ...resolver, fulfill: resolverFulfill }; + } ); + + const mapSelector = ( selector, selectorName ) => { + const resolver = resolvers[ selectorName ]; + if ( ! resolver ) { + return selector; + } + + return ( ...args ) => { + async function fulfillSelector() { + const state = namespace.store.getState(); + if ( typeof resolver.isFulfilled === 'function' && resolver.isFulfilled( state, ...args ) ) { + return; + } + + if ( fulfillment.hasStarted( selectorName, args ) ) { + return; + } + + fulfillment.start( selectorName, args ); + await fulfillment.fulfill( selectorName, ...args ); + fulfillment.finish( selectorName, args ); + } + + fulfillSelector( ...args ); + return selector( ...args ); + }; + }; + + namespace.selectors = mapValues( namespace.selectors, mapSelector ); +} diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index fe0a63318b913..c5f986f8c94bc 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -15,6 +15,7 @@ import { createNamespace, setActions, setSelectors, + setResolvers, } from './namespace-store.js'; import dataStore from './store'; import promise from './promise-middleware'; @@ -61,127 +62,6 @@ export function createRegistry( storeConfigs = {} ) { listeners.forEach( ( listener ) => listener() ); } - /** - * Registers a new sub-reducer to the global state and returns a Redux-like - * store object. - * - * @param {string} reducerKey Reducer key. - * @param {Object} reducer Reducer function. - * - * @return {Object} Store Object. - */ - function registerReducer( reducerKey, reducer ) { - const enhancers = [ - applyMiddleware( createResolversCacheMiddleware( registry, reducerKey ), promise ), - ]; - if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) { - enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: reducerKey, instanceId: reducerKey } ) ); - } - - // TODO: Remove globalListener from this call after subscriptions are passed. - namespaces[ reducerKey ] = createNamespace( reducer, enhancers, globalListener ); - return namespaces[ reducerKey ].store; - } - - /** - * Registers selectors for external usage. - * - * @param {string} reducerKey Part of the state shape to register the - * selectors for. - * @param {Object} newSelectors Selectors to register. Keys will be used as the - * public facing API. Selectors will get passed the - * state as first argument. - */ - function registerSelectors( reducerKey, newSelectors ) { - setSelectors( namespaces[ reducerKey ], newSelectors ); - } - - /** - * Registers resolvers for a given reducer key. Resolvers are side effects - * invoked once per argument set of a given selector call, used in ensuring - * that the data needs for the selector are satisfied. - * - * @param {string} reducerKey Part of the state shape to register the - * resolvers for. - * @param {Object} newResolvers Resolvers to register. - */ - function registerResolvers( reducerKey, newResolvers ) { - namespaces[ reducerKey ].resolvers = mapValues( newResolvers, ( resolver ) => { - const { fulfill: resolverFulfill = resolver } = resolver; - return { ...resolver, fulfill: resolverFulfill }; - } ); - - namespaces[ reducerKey ].selectors = mapValues( namespaces[ reducerKey ].selectors, ( selector, selectorName ) => { - const resolver = newResolvers[ selectorName ]; - if ( ! resolver ) { - return selector; - } - - return ( ...args ) => { - const { hasStartedResolution } = select( 'core/data' ); - const { startResolution, finishResolution } = dispatch( 'core/data' ); - async function fulfillSelector() { - const state = namespaces[ reducerKey ].store.getState(); - if ( typeof resolver.isFulfilled === 'function' && resolver.isFulfilled( state, ...args ) ) { - return; - } - - if ( hasStartedResolution( reducerKey, selectorName, args ) ) { - return; - } - - startResolution( reducerKey, selectorName, args ); - await fulfill( reducerKey, selectorName, ...args ); - finishResolution( reducerKey, selectorName, args ); - } - - fulfillSelector( ...args ); - return selector( ...args ); - }; - } ); - } - - /** - * Registers actions for external usage. - * - * @param {string} reducerKey Part of the state shape to register the - * selectors for. - * @param {Object} newActions Actions to register. - */ - function registerActions( reducerKey, newActions ) { - setActions( namespaces[ reducerKey ], newActions ); - } - - /** - * Convenience for registering reducer with actions and selectors. - * - * @param {string} reducerKey Reducer key. - * @param {Object} options Store description (reducer, actions, selectors, resolvers). - * - * @return {Object} Registered store object. - */ - function registerStore( reducerKey, options ) { - if ( ! options.reducer ) { - throw new TypeError( 'Must specify store reducer' ); - } - - const store = registerReducer( reducerKey, options.reducer ); - - if ( options.actions ) { - registerActions( reducerKey, options.actions ); - } - - if ( options.selectors ) { - registerSelectors( reducerKey, options.selectors ); - } - - if ( options.resolvers ) { - registerResolvers( reducerKey, options.resolvers ); - } - - return store; - } - /** * Subscribe to changes to any data. * @@ -210,7 +90,9 @@ export function createRegistry( storeConfigs = {} ) { } /** - * Calls a resolver given arguments + * Calls a resolver given arguments + * TODO: Move this out of the registry and into implementation. + * (after plugins are removed) * * @param {string} reducerKey Part of the state shape to register the * selectors for. @@ -218,7 +100,7 @@ export function createRegistry( storeConfigs = {} ) { * @param {Array} args Selector Arguments. */ async function fulfill( reducerKey, selectorName, ...args ) { - const resolver = get( namespaces, [ reducerKey, 'resolvers', selectorName ] ); + const resolver = get( registry.namespaces, [ reducerKey, 'resolvers', selectorName ] ); if ( ! resolver ) { return; } @@ -264,17 +146,111 @@ export function createRegistry( storeConfigs = {} ) { let registry = { namespaces, - registerReducer, - registerSelectors, - registerResolvers, - registerActions, - registerStore, subscribe, select, dispatch, use, }; + /** + * Registers a new sub-reducer to the global state and returns a Redux-like + * store object. + * + * @param {string} reducerKey Reducer key. + * @param {Object} reducer Reducer function. + * + * @return {Object} Store Object. + */ + registry.registerReducer = ( reducerKey, reducer ) => { + const enhancers = [ + applyMiddleware( createResolversCacheMiddleware( registry, reducerKey ), promise ), + ]; + if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) { + enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: reducerKey, instanceId: reducerKey } ) ); + } + + // TODO: Remove globalListener from this call after subscriptions are passed. + namespaces[ reducerKey ] = createNamespace( reducer, enhancers, globalListener ); + return namespaces[ reducerKey ].store; + }; + + /** + * Registers actions for external usage. + * + * @param {string} reducerKey Part of the state shape to register the + * selectors for. + * @param {Object} newActions Actions to register. + */ + registry.registerActions = ( reducerKey, newActions ) => { + setActions( namespaces[ reducerKey ], newActions ); + }; + + /** + * Registers selectors for external usage. + * + * @param {string} reducerKey Part of the state shape to register the + * selectors for. + * @param {Object} newSelectors Selectors to register. Keys will be used as the + * public facing API. Selectors will get passed the + * state as first argument. + */ + registry.registerSelectors = ( reducerKey, newSelectors ) => { + setSelectors( namespaces[ reducerKey ], newSelectors ); + }; + + /** + * Registers resolvers for a given reducer key. Resolvers are side effects + * invoked once per argument set of a given selector call, used in ensuring + * that the data needs for the selector are satisfied. + * + * @param {string} reducerKey Part of the state shape to register the + * resolvers for. + * @param {Object} newResolvers Resolvers to register. + */ + registry.registerResolvers = ( reducerKey, newResolvers ) => { + const { hasStartedResolution } = select( 'core/data' ); + const { startResolution, finishResolution } = dispatch( 'core/data' ); + + const fulfillment = { + hasStarted: ( ...args ) => hasStartedResolution( reducerKey, ...args ), + start: ( ...args ) => startResolution( reducerKey, ...args ), + finish: ( ...args ) => finishResolution( reducerKey, ...args ), + fulfill: ( ...args ) => fulfill( reducerKey, ...args ), + }; + + setResolvers( namespaces[ reducerKey ], newResolvers, fulfillment ); + }; + + /** + * Convenience for registering reducer with actions and selectors. + * + * @param {string} reducerKey Reducer key. + * @param {Object} options Store description (reducer, actions, selectors, resolvers). + * + * @return {Object} Registered store object. + */ + registry.registerStore = ( reducerKey, options ) => { + if ( ! options.reducer ) { + throw new TypeError( 'Must specify store reducer' ); + } + + const store = registry.registerReducer( reducerKey, options.reducer ); + + if ( options.actions ) { + registry.registerActions( reducerKey, options.actions ); + } + + if ( options.selectors ) { + registry.registerSelectors( reducerKey, options.selectors ); + } + + if ( options.resolvers ) { + registry.registerResolvers( reducerKey, options.resolvers ); + } + + return store; + }; + /** * Enhances the registry with the prescribed set of overrides. Returns the * enhanced registry to enable plugin chaining. @@ -296,7 +272,7 @@ export function createRegistry( storeConfigs = {} ) { Object.entries( { 'core/data': dataStore, ...storeConfigs, - } ).map( ( [ name, config ] ) => registerStore( name, config ) ); + } ).map( ( [ name, config ] ) => registry.registerStore( name, config ) ); return withPlugins( registry ); } From df3e25a82004e3f67ea1f207b152f42b77c685cc Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Fri, 5 Oct 2018 17:38:12 -0400 Subject: [PATCH 04/14] data: Centralize namespace registration This commit centralizes the logic for namespace registration, in order to support `registerStore` better. `register[Reducer|Actions|Selectors|Resolvers]` now are viewed as incomplete calls to `registerStore` --- packages/data/src/namespace-store.js | 196 +++++++++++++++++++++------ packages/data/src/registry.js | 88 ++---------- 2 files changed, 167 insertions(+), 117 deletions(-) diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index bb57de4633838..c2cba7665cb4e 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -1,83 +1,146 @@ /** * External dependencies */ -import { createStore as createReduxStore } from 'redux'; +import { createStore, applyMiddleware } from 'redux'; import { flowRight, + get, mapValues, } from 'lodash'; +/** + * Internal dependencies + */ +import promise from './promise-middleware'; +import createResolversCacheMiddleware from './resolvers-cache-middleware'; + /** * Creates a namespace object with a store derived from the reducer given. * - * @param {Object} reducer Reducer function. - * @param {Array} enhancers Array of enhancer functions to be added to the store. + * @param {string} key Identifying string used for namespace and redex dev tools. + * @param {Object} options Contains reducer, actions, selectors, and resolvers. + * @param {Object} registry Temporary registry reference, required for namespace updates. * @param {function} globalListener TODO: Remove this after subscribe is passed correctly. * * @return {Object} Store Object. */ -export function createNamespace( reducer, enhancers, globalListener ) { - const store = createReduxStore( reducer, flowRight( enhancers ) ); - const namespace = { store, reducer }; - - // TODO: Move this to a subscribe function instead of referencing globalListener. - // Customize subscribe behavior to call listeners only on effective change, - // not on every dispatch. - let lastState = store.getState(); - store.subscribe( () => { - const state = store.getState(); - const hasChanged = state !== lastState; - lastState = state; - - if ( hasChanged ) { - globalListener(); +export default function createNamespace( key, options, registry, globalListener ) { + // TODO: After register[Reducer|Actions|Selectors|Resolvers] are deprecated and removed, + // this function can be greatly simplified because it should no longer be called to modify + // a namespace, but only to create one, and only once for each namespace. + + let { + reducer, + store, + actions, + selectors, + resolvers, + } = registry.namespaces[ key ] || {}; + + if ( options.reducer ) { + reducer = options.reducer; + store = createReduxStore( reducer, key, registry ); + + // TODO: Move this to a subscribe function instead of referencing globalListener. + // Customize subscribe behavior to call listeners only on effective change, + // not on every dispatch. + let lastState = store.getState(); + store.subscribe( () => { + const state = store.getState(); + const hasChanged = state !== lastState; + lastState = state; + + if ( hasChanged ) { + globalListener(); + } + } ); + } + if ( options.actions ) { + if ( ! store ) { + throw new TypeError( 'Cannot specify actions when no reducer is present' ); } - } ); + actions = mapActions( options.actions, store ); + } + if ( options.selectors ) { + if ( ! store ) { + throw new TypeError( 'Cannot specify selectors when no reducer is present' ); + } + selectors = mapSelectors( options.selectors, store ); + } + if ( options.resolvers ) { + const fulfillment = getCoreDataFulfillment( registry, key ); + const result = mapResolvers( options.resolvers, selectors, fulfillment, store ); + resolvers = result.resolvers; + selectors = result.selectors; + } - return namespace; + return { + reducer, + store, + actions, + selectors, + resolvers, + }; } /** - * Sets selectors for given namespace. + * Creates a redux store for a namespace. + * + * @param {Function} reducer Root reducer for redux store. + * @param {string} key Part of the state shape to register the + * selectors for. + * @param {Object} registry Registry reference, for resolver enhancer support. + * @return {Object} Newly created redux store. + */ +function createReduxStore( reducer, key, registry ) { + const enhancers = [ + applyMiddleware( createResolversCacheMiddleware( registry, key ), promise ), + ]; + if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) { + enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: key, instanceId: key } ) ); + } + + return createStore( reducer, flowRight( enhancers ) ); +} + +/** + * Maps selectors to a redux store. * - * @param {Object} namespace The namespace object to modify. * @param {Object} selectors Selectors to register. Keys will be used as the * public facing API. Selectors will get passed the * state as first argument. + * @param {Object} store The redux store to which the selectors should be mapped. + * @return {Object} Selectors mapped to the redux store provided. */ -export function setSelectors( namespace, selectors ) { - const { store } = namespace; +function mapSelectors( selectors, store ) { const createStateSelector = ( selector ) => ( ...args ) => selector( store.getState(), ...args ); - namespace.selectors = mapValues( selectors, createStateSelector ); + return mapValues( selectors, createStateSelector ); } /** - * Sets actions for given namespace. + * Maps actions to dispatch from a given store. * - * @param {Object} namespace The namespace object to modify. * @param {Object} actions Actions to register. + * @param {Object} store The redux store to which the actions should be mapped. + * @return {Object} Actions mapped to the redux store provided. */ -export function setActions( namespace, actions ) { - const { store } = namespace; +function mapActions( actions, store ) { const createBoundAction = ( action ) => ( ...args ) => store.dispatch( action( ...args ) ); - namespace.actions = mapValues( actions, createBoundAction ); + return mapValues( actions, createBoundAction ); } /** - * Sets resolvers for a given namespace. Resolvers are side effects - * invoked once per argument set of a given selector call, used in ensuring - * that the data needs for the selector are satisfied. + * Returns resolvers with matched selectors for a given namespace. + * Resolvers are side effects invoked once per argument set of a given selector call, + * used in ensuring that the data needs for the selector are satisfied. * - * @param {Object} namespace The namespace object to modify. * @param {Object} resolvers Resolvers to register. + * @param {Object} selectors The current selectors to be modified. * @param {Object} fulfillment Fulfillment implementation functions. + * @param {Object} store The redux store to which the resolvers should be mapped. + * @return {Object} An object containing updated selectors and resolvers. */ -export function setResolvers( namespace, resolvers, fulfillment ) { - namespace.resolvers = mapValues( resolvers, ( resolver ) => { - const { fulfill: resolverFulfill = resolver } = resolver; - return { ...resolver, fulfill: resolverFulfill }; - } ); - +function mapResolvers( resolvers, selectors, fulfillment, store ) { const mapSelector = ( selector, selectorName ) => { const resolver = resolvers[ selectorName ]; if ( ! resolver ) { @@ -86,7 +149,7 @@ export function setResolvers( namespace, resolvers, fulfillment ) { return ( ...args ) => { async function fulfillSelector() { - const state = namespace.store.getState(); + const state = store.getState(); if ( typeof resolver.isFulfilled === 'function' && resolver.isFulfilled( state, ...args ) ) { return; } @@ -105,5 +168,54 @@ export function setResolvers( namespace, resolvers, fulfillment ) { }; }; - namespace.selectors = mapValues( namespace.selectors, mapSelector ); + const mappedResolvers = mapValues( resolvers, ( resolver ) => { + const { fulfill: resolverFulfill = resolver } = resolver; + return { ...resolver, fulfill: resolverFulfill }; + } ); + + return { + resolvers: mappedResolvers, + selectors: mapValues( selectors, mapSelector ), + }; +} + +/** + * Bundles up fulfillment functions for resolvers. + * @param {Object} registry Registry reference, for fulfilling via resolvers + * @param {string} key Part of the state shape to register the + * selectors for. + * @return {Object} An object providing fulfillment functions. + */ +function getCoreDataFulfillment( registry, key ) { + const { hasStartedResolution } = registry.select( 'core/data' ); + const { startResolution, finishResolution } = registry.dispatch( 'core/data' ); + + return { + hasStarted: ( ...args ) => hasStartedResolution( key, ...args ), + start: ( ...args ) => startResolution( key, ...args ), + finish: ( ...args ) => finishResolution( key, ...args ), + fulfill: ( ...args ) => fulfillWithRegistry( registry, key, ...args ), + }; +} + +/** + * Calls a resolver given arguments + * + * @param {Object} registry Registry reference, for fulfilling via resolvers + * @param {string} key Part of the state shape to register the + * selectors for. + * @param {string} selectorName Selector name to fulfill. + * @param {Array} args Selector Arguments. + */ +async function fulfillWithRegistry( registry, key, selectorName, ...args ) { + const resolver = get( registry.namespaces, [ key, 'resolvers', selectorName ] ); + if ( ! resolver ) { + return; + } + + const store = registry.namespaces[ key ].store; + const action = resolver.fulfill( ...args ); + if ( action ) { + await store.dispatch( action ); + } } diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index c5f986f8c94bc..4f9a30d9fa117 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -6,20 +6,12 @@ import { mapValues, get, } from 'lodash'; -import { applyMiddleware } from 'redux'; /** * Internal dependencies */ -import { - createNamespace, - setActions, - setSelectors, - setResolvers, -} from './namespace-store.js'; +import createNamespace from './namespace-store.js'; import dataStore from './store'; -import promise from './promise-middleware'; -import createResolversCacheMiddleware from './resolvers-cache-middleware'; /** * An isolated orchestrator of store registrations. @@ -89,29 +81,6 @@ export function createRegistry( storeConfigs = {} ) { return get( namespaces, [ reducerKey, 'selectors' ] ); } - /** - * Calls a resolver given arguments - * TODO: Move this out of the registry and into implementation. - * (after plugins are removed) - * - * @param {string} reducerKey Part of the state shape to register the - * selectors for. - * @param {string} selectorName Selector name to fulfill. - * @param {Array} args Selector Arguments. - */ - async function fulfill( reducerKey, selectorName, ...args ) { - const resolver = get( registry.namespaces, [ reducerKey, 'resolvers', selectorName ] ); - if ( ! resolver ) { - return; - } - - const store = namespaces[ reducerKey ].store; - const action = resolver.fulfill( ...args ); - if ( action ) { - await store.dispatch( action ); - } - } - /** * Returns the available actions for a part of the state. * @@ -162,15 +131,7 @@ export function createRegistry( storeConfigs = {} ) { * @return {Object} Store Object. */ registry.registerReducer = ( reducerKey, reducer ) => { - const enhancers = [ - applyMiddleware( createResolversCacheMiddleware( registry, reducerKey ), promise ), - ]; - if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) { - enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: reducerKey, instanceId: reducerKey } ) ); - } - - // TODO: Remove globalListener from this call after subscriptions are passed. - namespaces[ reducerKey ] = createNamespace( reducer, enhancers, globalListener ); + namespaces[ reducerKey ] = createNamespace( reducerKey, { reducer }, registry, globalListener ); return namespaces[ reducerKey ].store; }; @@ -179,10 +140,10 @@ export function createRegistry( storeConfigs = {} ) { * * @param {string} reducerKey Part of the state shape to register the * selectors for. - * @param {Object} newActions Actions to register. + * @param {Object} actions Actions to register. */ - registry.registerActions = ( reducerKey, newActions ) => { - setActions( namespaces[ reducerKey ], newActions ); + registry.registerActions = ( reducerKey, actions ) => { + namespaces[ reducerKey ] = createNamespace( reducerKey, { actions }, registry, globalListener ); }; /** @@ -190,12 +151,12 @@ export function createRegistry( storeConfigs = {} ) { * * @param {string} reducerKey Part of the state shape to register the * selectors for. - * @param {Object} newSelectors Selectors to register. Keys will be used as the + * @param {Object} selectors Selectors to register. Keys will be used as the * public facing API. Selectors will get passed the * state as first argument. */ - registry.registerSelectors = ( reducerKey, newSelectors ) => { - setSelectors( namespaces[ reducerKey ], newSelectors ); + registry.registerSelectors = ( reducerKey, selectors ) => { + namespaces[ reducerKey ] = createNamespace( reducerKey, { selectors }, registry, globalListener ); }; /** @@ -205,20 +166,10 @@ export function createRegistry( storeConfigs = {} ) { * * @param {string} reducerKey Part of the state shape to register the * resolvers for. - * @param {Object} newResolvers Resolvers to register. + * @param {Object} resolvers Resolvers to register. */ - registry.registerResolvers = ( reducerKey, newResolvers ) => { - const { hasStartedResolution } = select( 'core/data' ); - const { startResolution, finishResolution } = dispatch( 'core/data' ); - - const fulfillment = { - hasStarted: ( ...args ) => hasStartedResolution( reducerKey, ...args ), - start: ( ...args ) => startResolution( reducerKey, ...args ), - finish: ( ...args ) => finishResolution( reducerKey, ...args ), - fulfill: ( ...args ) => fulfill( reducerKey, ...args ), - }; - - setResolvers( namespaces[ reducerKey ], newResolvers, fulfillment ); + registry.registerResolvers = ( reducerKey, resolvers ) => { + namespaces[ reducerKey ] = createNamespace( reducerKey, { resolvers }, registry, globalListener ); }; /** @@ -234,21 +185,8 @@ export function createRegistry( storeConfigs = {} ) { throw new TypeError( 'Must specify store reducer' ); } - const store = registry.registerReducer( reducerKey, options.reducer ); - - if ( options.actions ) { - registry.registerActions( reducerKey, options.actions ); - } - - if ( options.selectors ) { - registry.registerSelectors( reducerKey, options.selectors ); - } - - if ( options.resolvers ) { - registry.registerResolvers( reducerKey, options.resolvers ); - } - - return store; + namespaces[ reducerKey ] = createNamespace( reducerKey, options, registry, globalListener ); + return namespaces[ reducerKey ].store; }; /** From 1063e3de0ee01590cdbd3e5c2103b597234b5443 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Sat, 6 Oct 2018 18:00:01 -0400 Subject: [PATCH 05/14] data: Add registerGenericStore This adds the registerGenericStore function and removes the `namespaces` object in favor of a more generic `stores` object. --- packages/data/src/namespace-store.js | 15 ++++++-- packages/data/src/registry.js | 52 +++++++++++++++++++++------- packages/data/src/test/registry.js | 18 +++++++++- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index c2cba7665cb4e..eb66b5aad3350 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -29,6 +29,8 @@ export default function createNamespace( key, options, registry, globalListener // this function can be greatly simplified because it should no longer be called to modify // a namespace, but only to create one, and only once for each namespace. + // TODO: After removing `registry.namespaces`and making stores immutable after create, + // reducer, store, actinos, selectors, and resolvers can all be removed from here. let { reducer, store, @@ -74,12 +76,19 @@ export default function createNamespace( key, options, registry, globalListener selectors = result.selectors; } + const getSelectors = () => selectors; + const getActions = () => actions; + const subscribe = store && store.subscribe; + return { reducer, store, actions, selectors, resolvers, + getSelectors, + getActions, + subscribe, }; } @@ -208,14 +217,14 @@ function getCoreDataFulfillment( registry, key ) { * @param {Array} args Selector Arguments. */ async function fulfillWithRegistry( registry, key, selectorName, ...args ) { - const resolver = get( registry.namespaces, [ key, 'resolvers', selectorName ] ); + const namespace = registry.stores[ key ]; + const resolver = get( namespace, [ 'resolvers', selectorName ] ); if ( ! resolver ) { return; } - const store = registry.namespaces[ key ].store; const action = resolver.fulfill( ...args ); if ( action ) { - await store.dispatch( action ); + await namespace.store.dispatch( action ); } } diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index 4f9a30d9fa117..cabaecc34632e 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -4,7 +4,6 @@ import { without, mapValues, - get, } from 'lodash'; /** @@ -44,7 +43,7 @@ import dataStore from './store'; * @return {WPDataRegistry} Data registry. */ export function createRegistry( storeConfigs = {} ) { - const namespaces = {}; + const stores = {}; let listeners = []; /** @@ -78,7 +77,8 @@ export function createRegistry( storeConfigs = {} ) { * @return {*} The selector's returned value. */ function select( reducerKey ) { - return get( namespaces, [ reducerKey, 'selectors' ] ); + const store = stores[ reducerKey ]; + return store && store.getSelectors(); } /** @@ -90,7 +90,8 @@ export function createRegistry( storeConfigs = {} ) { * @return {*} The action's returned value. */ function dispatch( reducerKey ) { - return get( namespaces, [ reducerKey, 'actions' ] ); + const store = stores[ reducerKey ]; + return store && store.getActions(); } /** @@ -114,7 +115,8 @@ export function createRegistry( storeConfigs = {} ) { } let registry = { - namespaces, + stores, + namespaces: stores, // TODO: Deprecate/remove this. subscribe, select, dispatch, @@ -131,8 +133,9 @@ export function createRegistry( storeConfigs = {} ) { * @return {Object} Store Object. */ registry.registerReducer = ( reducerKey, reducer ) => { - namespaces[ reducerKey ] = createNamespace( reducerKey, { reducer }, registry, globalListener ); - return namespaces[ reducerKey ].store; + const namespace = createNamespace( reducerKey, { reducer }, registry, globalListener ); + registerGenericStore( reducerKey, namespace ); + return namespace.store; }; /** @@ -143,7 +146,8 @@ export function createRegistry( storeConfigs = {} ) { * @param {Object} actions Actions to register. */ registry.registerActions = ( reducerKey, actions ) => { - namespaces[ reducerKey ] = createNamespace( reducerKey, { actions }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { actions }, registry, globalListener ); + registerGenericStore( reducerKey, namespace ); }; /** @@ -156,7 +160,8 @@ export function createRegistry( storeConfigs = {} ) { * state as first argument. */ registry.registerSelectors = ( reducerKey, selectors ) => { - namespaces[ reducerKey ] = createNamespace( reducerKey, { selectors }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { selectors }, registry, globalListener ); + registerGenericStore( reducerKey, namespace ); }; /** @@ -169,11 +174,12 @@ export function createRegistry( storeConfigs = {} ) { * @param {Object} resolvers Resolvers to register. */ registry.registerResolvers = ( reducerKey, resolvers ) => { - namespaces[ reducerKey ] = createNamespace( reducerKey, { resolvers }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { resolvers }, registry, globalListener ); + registerGenericStore( reducerKey, namespace ); }; /** - * Convenience for registering reducer with actions and selectors. + * Registers a standard `@wordpress/data` store. * * @param {string} reducerKey Reducer key. * @param {Object} options Store description (reducer, actions, selectors, resolvers). @@ -185,10 +191,30 @@ export function createRegistry( storeConfigs = {} ) { throw new TypeError( 'Must specify store reducer' ); } - namespaces[ reducerKey ] = createNamespace( reducerKey, options, registry, globalListener ); - return namespaces[ reducerKey ].store; + const namespace = createNamespace( reducerKey, options, registry, globalListener ); + registerGenericStore( reducerKey, namespace ); + return namespace.store; }; + /** + * Registers a generic store. + * + * @param {string} key Store registry key. + * @param {Object} config Configuration (getSelectors, getActions, subscribe). + */ + function registerGenericStore( key, config ) { + if ( typeof config.getSelectors !== 'function' ) { + throw new TypeError( 'config.getSelectors must be a function' ); + } + if ( typeof config.getActions !== 'function' ) { + throw new TypeError( 'config.getActions must be a function' ); + } + if ( ! config.subscribe ) { + throw new TypeError( 'config.subscribe must be a function' ); + } + stores[ key ] = config; + } + /** * Enhances the registry with the prescribed set of overrides. Returns the * enhanced registry to enable plugin chaining. diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index 7e6fd4a8d4990..d92e6130061c7 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -40,6 +40,18 @@ describe( 'createRegistry', () => { } } ); + describe( 'registerGenericStore', () => { + it( 'should throw if not all required config elements are present', () => { + const getSelectors = () => ( {} ); + const getActions = () => ( {} ); + const subscribe = () => ( {} ); + + expect( () => registry.registerStore( 'grocer', {} ) ).toThrow(); + expect( () => registry.registerStore( 'grocer', { getSelectors, getActions } ) ).toThrow(); + expect( () => registry.registerStore( 'grocer', { getActions, subscribe } ) ).toThrow(); + } ); + } ); + describe( 'registerStore', () => { it( 'should be shorthand for reducer, actions, selectors registration', () => { const store = registry.registerStore( 'butcher', { @@ -424,9 +436,13 @@ describe( 'createRegistry', () => { // function proxying. expect( _registry ).toMatchObject( mapValues( registry, ( value, key ) => { - if ( key === 'namespaces' ) { + if ( key === 'stores' ) { return expect.any( Object ); } + // TODO: Remove this after namsespaces is removed. + if ( key === 'namespaces' ) { + return registry.stores; + } return expect.any( Function ); } ) ); From 017ae804f69e18d42c36444fcb5ccfcda431372c Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Sat, 6 Oct 2018 18:11:37 -0400 Subject: [PATCH 06/14] data: Use store.subscribe() function --- packages/data/src/namespace-store.js | 33 ++++++++++++++-------------- packages/data/src/registry.js | 11 +++++----- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/data/src/namespace-store.js b/packages/data/src/namespace-store.js index eb66b5aad3350..5aa68bc928935 100644 --- a/packages/data/src/namespace-store.js +++ b/packages/data/src/namespace-store.js @@ -20,11 +20,10 @@ import createResolversCacheMiddleware from './resolvers-cache-middleware'; * @param {string} key Identifying string used for namespace and redex dev tools. * @param {Object} options Contains reducer, actions, selectors, and resolvers. * @param {Object} registry Temporary registry reference, required for namespace updates. - * @param {function} globalListener TODO: Remove this after subscribe is passed correctly. * * @return {Object} Store Object. */ -export default function createNamespace( key, options, registry, globalListener ) { +export default function createNamespace( key, options, registry ) { // TODO: After register[Reducer|Actions|Selectors|Resolvers] are deprecated and removed, // this function can be greatly simplified because it should no longer be called to modify // a namespace, but only to create one, and only once for each namespace. @@ -42,20 +41,6 @@ export default function createNamespace( key, options, registry, globalListener if ( options.reducer ) { reducer = options.reducer; store = createReduxStore( reducer, key, registry ); - - // TODO: Move this to a subscribe function instead of referencing globalListener. - // Customize subscribe behavior to call listeners only on effective change, - // not on every dispatch. - let lastState = store.getState(); - store.subscribe( () => { - const state = store.getState(); - const hasChanged = state !== lastState; - lastState = state; - - if ( hasChanged ) { - globalListener(); - } - } ); } if ( options.actions ) { if ( ! store ) { @@ -78,7 +63,21 @@ export default function createNamespace( key, options, registry, globalListener const getSelectors = () => selectors; const getActions = () => actions; - const subscribe = store && store.subscribe; + + // Customize subscribe behavior to call listeners only on effective change, + // not on every dispatch. + const subscribe = store && function( listener ) { + let lastState = store.getState(); + store.subscribe( () => { + const state = store.getState(); + const hasChanged = state !== lastState; + lastState = state; + + if ( hasChanged ) { + listener(); + } + } ); + }; return { reducer, diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index cabaecc34632e..98819a130658a 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -133,7 +133,7 @@ export function createRegistry( storeConfigs = {} ) { * @return {Object} Store Object. */ registry.registerReducer = ( reducerKey, reducer ) => { - const namespace = createNamespace( reducerKey, { reducer }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { reducer }, registry ); registerGenericStore( reducerKey, namespace ); return namespace.store; }; @@ -146,7 +146,7 @@ export function createRegistry( storeConfigs = {} ) { * @param {Object} actions Actions to register. */ registry.registerActions = ( reducerKey, actions ) => { - const namespace = createNamespace( reducerKey, { actions }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { actions }, registry ); registerGenericStore( reducerKey, namespace ); }; @@ -160,7 +160,7 @@ export function createRegistry( storeConfigs = {} ) { * state as first argument. */ registry.registerSelectors = ( reducerKey, selectors ) => { - const namespace = createNamespace( reducerKey, { selectors }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { selectors }, registry ); registerGenericStore( reducerKey, namespace ); }; @@ -174,7 +174,7 @@ export function createRegistry( storeConfigs = {} ) { * @param {Object} resolvers Resolvers to register. */ registry.registerResolvers = ( reducerKey, resolvers ) => { - const namespace = createNamespace( reducerKey, { resolvers }, registry, globalListener ); + const namespace = createNamespace( reducerKey, { resolvers }, registry ); registerGenericStore( reducerKey, namespace ); }; @@ -191,7 +191,7 @@ export function createRegistry( storeConfigs = {} ) { throw new TypeError( 'Must specify store reducer' ); } - const namespace = createNamespace( reducerKey, options, registry, globalListener ); + const namespace = createNamespace( reducerKey, options, registry ); registerGenericStore( reducerKey, namespace ); return namespace.store; }; @@ -213,6 +213,7 @@ export function createRegistry( storeConfigs = {} ) { throw new TypeError( 'config.subscribe must be a function' ); } stores[ key ] = config; + config.subscribe( globalListener ); } /** From 02652a1456e0e0a563b0fbebb0cdc5ccc9670d3d Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Wed, 17 Oct 2018 10:06:39 -0500 Subject: [PATCH 07/14] data: Make registerGenericStore public + add tests This makes `registry.registerGenericStore` accessible from the result of `createRegistry`, and adds tests for the functionality of it. --- packages/data/src/registry.js | 47 +++++++------ packages/data/src/test/registry.js | 107 +++++++++++++++++++++++++++-- 2 files changed, 128 insertions(+), 26 deletions(-) diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index 98819a130658a..f242f28e2333f 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -98,6 +98,7 @@ export function createRegistry( storeConfigs = {} ) { * Maps an object of function values to proxy invocation through to the * current internal representation of the registry, which may be enhanced * by plugins. + * TODO: Consider deprecating and removing this after plugins are no longer needed. * * @param {Object} attributes Object of function values. * @@ -114,7 +115,28 @@ export function createRegistry( storeConfigs = {} ) { } ); } + /** + * Registers a generic store. + * + * @param {string} key Store registry key. + * @param {Object} config Configuration (getSelectors, getActions, subscribe). + */ + function registerGenericStore( key, config ) { + if ( typeof config.getSelectors !== 'function' ) { + throw new TypeError( 'config.getSelectors must be a function' ); + } + if ( typeof config.getActions !== 'function' ) { + throw new TypeError( 'config.getActions must be a function' ); + } + if ( typeof config.subscribe !== 'function' ) { + throw new TypeError( 'config.subscribe must be a function' ); + } + stores[ key ] = config; + config.subscribe( globalListener ); + } + let registry = { + registerGenericStore, stores, namespaces: stores, // TODO: Deprecate/remove this. subscribe, @@ -126,6 +148,7 @@ export function createRegistry( storeConfigs = {} ) { /** * Registers a new sub-reducer to the global state and returns a Redux-like * store object. + * TODO: Deprecate and remove this function in favor of registerStore * * @param {string} reducerKey Reducer key. * @param {Object} reducer Reducer function. @@ -140,6 +163,7 @@ export function createRegistry( storeConfigs = {} ) { /** * Registers actions for external usage. + * TODO: Deprecate and remove this function in favor of registerStore * * @param {string} reducerKey Part of the state shape to register the * selectors for. @@ -152,6 +176,7 @@ export function createRegistry( storeConfigs = {} ) { /** * Registers selectors for external usage. + * TODO: Deprecate and remove this function in favor of registerStore * * @param {string} reducerKey Part of the state shape to register the * selectors for. @@ -168,6 +193,7 @@ export function createRegistry( storeConfigs = {} ) { * Registers resolvers for a given reducer key. Resolvers are side effects * invoked once per argument set of a given selector call, used in ensuring * that the data needs for the selector are satisfied. + * TODO: Deprecate and remove this function in favor of registerStore * * @param {string} reducerKey Part of the state shape to register the * resolvers for. @@ -196,29 +222,10 @@ export function createRegistry( storeConfigs = {} ) { return namespace.store; }; - /** - * Registers a generic store. - * - * @param {string} key Store registry key. - * @param {Object} config Configuration (getSelectors, getActions, subscribe). - */ - function registerGenericStore( key, config ) { - if ( typeof config.getSelectors !== 'function' ) { - throw new TypeError( 'config.getSelectors must be a function' ); - } - if ( typeof config.getActions !== 'function' ) { - throw new TypeError( 'config.getActions must be a function' ); - } - if ( ! config.subscribe ) { - throw new TypeError( 'config.subscribe must be a function' ); - } - stores[ key ] = config; - config.subscribe( globalListener ); - } - /** * Enhances the registry with the prescribed set of overrides. Returns the * enhanced registry to enable plugin chaining. + * TODO: Consider deprecating and removing this after plugins are no longer needed. * * @param {WPDataPlugin} plugin Plugin by which to enhance. * @param {?Object} options Optional options to pass to plugin. diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index d92e6130061c7..2663905e359ca 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -41,14 +41,109 @@ describe( 'createRegistry', () => { } ); describe( 'registerGenericStore', () => { + let getSelectors; + let getActions; + let subscribe; + + beforeEach( () => { + getSelectors = () => ( {} ); + getActions = () => ( {} ); + subscribe = () => ( {} ); + } ); + it( 'should throw if not all required config elements are present', () => { - const getSelectors = () => ( {} ); - const getActions = () => ( {} ); - const subscribe = () => ( {} ); + expect( () => registry.registerGenericStore( 'grocer', {} ) ).toThrow(); + expect( () => registry.registerGenericStore( 'grocer', { getSelectors, getActions } ) ).toThrow(); + expect( () => registry.registerGenericStore( 'grocer', { getActions, subscribe } ) ).toThrow(); + } ); + + describe( 'getSelectors', () => { + it( 'should make selectors available via registry.select', () => { + const items = { + broccoli: { price: 2, quantity: 15 }, + lettuce: { price: 1, quantity: 12 }, + }; + + function getPrice( itemName ) { + const item = items[ itemName ]; + return item && item.price; + } + + function getQuantity( itemName ) { + const item = items[ itemName ]; + return item && item.quantity; + } + + getSelectors = () => ( { getPrice, getQuantity } ); + + registry.registerGenericStore( 'grocer', { getSelectors, getActions, subscribe } ); + + expect( registry.select( 'grocer' ).getPrice ).toEqual( getPrice ); + expect( registry.select( 'grocer' ).getQuantity ).toEqual( getQuantity ); + } ); + } ); + + describe( 'getActions', () => { + it( 'should make actions available via registry.dispatch', () => { + const dispatch = jest.fn(); - expect( () => registry.registerStore( 'grocer', {} ) ).toThrow(); - expect( () => registry.registerStore( 'grocer', { getSelectors, getActions } ) ).toThrow(); - expect( () => registry.registerStore( 'grocer', { getActions, subscribe } ) ).toThrow(); + function setPrice( itemName, price ) { + return { type: 'SET_PRICE', itemName, price }; + } + + function setQuantity( itemName, quantity ) { + return { type: 'SET_QUANTITY', itemName, quantity }; + } + + getActions = () => { + return { + setPrice: ( ...args ) => dispatch( setPrice( ...args ) ), + setQuantity: ( ...args ) => dispatch( setQuantity( ...args ) ), + }; + }; + + registry.registerGenericStore( 'grocer', { getSelectors, getActions, subscribe } ); + + expect( dispatch ).not.toHaveBeenCalled(); + + registry.dispatch( 'grocer' ).setPrice( 'broccoli', 3 ); + expect( dispatch ).toHaveBeenCalledTimes( 1 ); + expect( dispatch ).toHaveBeenCalledWith( + { type: 'SET_PRICE', itemName: 'broccoli', price: 3 } + ); + + registry.dispatch( 'grocer' ).setQuantity( 'lettuce', 8 ); + expect( dispatch ).toHaveBeenCalledTimes( 2 ); + expect( dispatch ).toHaveBeenCalledWith( + { type: 'SET_QUANTITY', itemName: 'lettuce', quantity: 8 } + ); + } ); + } ); + + describe( 'subscribe', () => { + it( 'should send out updates to listeners of the registry', () => { + const registryListener = jest.fn(); + + let listener = () => {}; + const storeChanged = () => { + listener(); + }; + subscribe = ( newListener ) => { + listener = newListener; + }; + + const unsubscribe = registry.subscribe( registryListener ); + registry.registerGenericStore( 'grocer', { getSelectors, getActions, subscribe } ); + + expect( registryListener ).not.toHaveBeenCalled(); + storeChanged(); + expect( registryListener ).toHaveBeenCalledTimes( 1 ); + storeChanged(); + expect( registryListener ).toHaveBeenCalledTimes( 2 ); + unsubscribe(); + storeChanged(); + expect( registryListener ).toHaveBeenCalledTimes( 2 ); + } ); } ); } ); From e2683eb9659614947ba7682c05c858978f88fd3a Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Wed, 17 Oct 2018 10:54:06 -0500 Subject: [PATCH 08/14] data: Add registerGenericStore to readme This adds information about `registerGenericStore` to the readme with two examples. --- packages/data/README.md | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/packages/data/README.md b/packages/data/README.md index 424ddd91c5fb1..df0665c07a3ae 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -286,6 +286,93 @@ const SaleButton = withDispatch( ( dispatch, ownProps ) => { // Start Sale! ``` +## Generic Stores + +The `@wordpress/data` module offers a more advanced and generic interface for the purposes of integrating other data systems and situations where more direct control over a data system is needed. In this case, a data store will need to be implemented outside of `@wordpress/data` and then plugged in via three functions: + +- `getSelectors()`: Returns an object of selector functions, pre-mapped to the store. +- `getActions()`: Returns an object of action functions, pre-mapped to the store. +- `subscribe( listener: Function )`: Registers a function called any time the value of state changes. + - Behaves as Redux [`subscribe`](https://redux.js.org/api-reference/store#subscribe(listener)) + with the following differences: + - Doesn't have to implement an unsubscribe, since the registry never uses it. + - Only has to support one listener (the registry). + +By implementing the above interface for your custom store, you gain the benefits of using the registry and the `withSelect` and `withDispatch` higher order components in your application code. This provides seamless integration with existing and alternative data systems. + +Integrating an existing redux store with its own reducers, store enhancers and middleware can be accomplished as follows: + +_Example:_ + +```js +import existingSelectors from './existing-app/selectors'; +import existingActions from './existing-app/actions'; +import createStore from './existing-app/store'; + +const reduxStore = createStore(); + +const mappedSelectors = existingSelectors.map( ( selector ) => { + return ( ...args ) => selector( reduxStore.getState(), ...args ); +} ); + +const mappedActions = existingActions.map( ( action ) => { + return actions.map( ( action ) => { + return ( ...args ) => reduxStore.dispatch( action( ...args ) ); + } ); +} ); + +const genericStore = { + getSelectors(): { + return mappedSelectors; + }, + getActions(): { + return mappedActions; + }, + subscribe: reduxStore.subscribe; +}; + +registry.registerGenericStore( 'existing-app', genericStore ); +``` + +It is also possible to impelement a completely custom store from scratch: + +_Example:_ + +```js +function createCustomStore() { + let storeChanged = () => {}; + const prices = { hammer: 7.50 }; + + const selectors = { + getPrice( itemName ): { + return prices[ itemName ]; + }, + }; + + const actions = { + setPrice( itemName, price ): { + prices[ itemName ] = price; + storeChanged(); + }, + }; + + return { + getSelectors(): { + return selectors; + }, + getActions(): { + return actions; + }, + subscribe( listener ): { + storeChanged = listener; + } + }; +} + +registry.registerGenericStore( 'custom-data', customStore ); +``` + + ## Comparison with Redux The data module shares many of the same [core principles](https://redux.js.org/introduction/three-principles) and [API method naming](https://redux.js.org/api-reference) of [Redux](https://redux.js.org/). In fact, it is implemented atop Redux. Where it differs is in establishing a modularization pattern for creating separate but interdependent stores, and in codifying conventions such as selector functions as the primary entry point for data access. From 71c00ae8938d63d4cee502bb6cf46b2eea05f7a1 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Wed, 17 Oct 2018 13:53:30 -0500 Subject: [PATCH 09/14] data: Export registerGenericStore from index This adds an export for `registerGenericStore` for the defaultRegistry. While the defaultRegistry may be going away in the future, it's here for now, so this should be there until it changes. --- packages/data/src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 122f9c261b760..e521d5f14842b 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -30,6 +30,7 @@ export { combineReducers }; export const select = defaultRegistry.select; export const dispatch = defaultRegistry.dispatch; export const subscribe = defaultRegistry.subscribe; +export const registerGenericStore = defaultRegistry.registerGenericStore; export const registerStore = defaultRegistry.registerStore; export const registerReducer = defaultRegistry.registerReducer; export const registerActions = defaultRegistry.registerActions; From 2b4235b8dc35f27fe1c5223be94817376b043737 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Thu, 18 Oct 2018 08:42:40 -0500 Subject: [PATCH 10/14] data: Fixes to readme --- packages/data/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index df0665c07a3ae..dfe9783d830d6 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -322,10 +322,10 @@ const mappedActions = existingActions.map( ( action ) => { } ); const genericStore = { - getSelectors(): { + getSelectors() { return mappedSelectors; }, - getActions(): { + getActions() { return mappedActions; }, subscribe: reduxStore.subscribe; @@ -334,7 +334,7 @@ const genericStore = { registry.registerGenericStore( 'existing-app', genericStore ); ``` -It is also possible to impelement a completely custom store from scratch: +It is also possible to implement a completely custom store from scratch: _Example:_ @@ -357,19 +357,19 @@ function createCustomStore() { }; return { - getSelectors(): { + getSelectors() { return selectors; }, - getActions(): { + getActions() { return actions; }, - subscribe( listener ): { + subscribe( listener ) { storeChanged = listener; } }; } -registry.registerGenericStore( 'custom-data', customStore ); +registry.registerGenericStore( 'custom-data', createCustomStore() ); ``` From 92803e3e392bcbcc73c04724070d256946a71975 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Thu, 1 Nov 2018 13:33:43 -0500 Subject: [PATCH 11/14] data: Deprecate registerX and use functions This deprecates the following functions from the registry: - registerReducer - registerSelectors - registerActions - registerResolvers - use registerStore should be used instead of the above register functions, and registerGenericStore should be used instead of plugins. --- package-lock.json | 1 + packages/data/package.json | 1 + .../src/plugins/persistence/test/index.js | 4 + packages/data/src/registry.js | 118 ++++++++---------- packages/data/src/test/registry.js | 14 +++ 5 files changed, 75 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 861bc2d262477..ebac1b4557be4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2183,6 +2183,7 @@ "requires": { "@babel/runtime": "^7.0.0", "@wordpress/compose": "file:packages/compose", + "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/element": "file:packages/element", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/redux-routine": "file:packages/redux-routine", diff --git a/packages/data/package.json b/packages/data/package.json index 596cad5d3a3c8..e848ec8cd8bf0 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -23,6 +23,7 @@ "dependencies": { "@babel/runtime": "^7.0.0", "@wordpress/compose": "file:../compose", + "@wordpress/deprecated": "file:../deprecated", "@wordpress/element": "file:../element", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/redux-routine": "file:../redux-routine", diff --git a/packages/data/src/plugins/persistence/test/index.js b/packages/data/src/plugins/persistence/test/index.js index c614e624d2191..f352bd84ae7cb 100644 --- a/packages/data/src/plugins/persistence/test/index.js +++ b/packages/data/src/plugins/persistence/test/index.js @@ -21,6 +21,7 @@ describe( 'persistence', () => { // Since the exposed `registerStore` is a proxying function, mimic // intercept of original call by adding an initial plugin. + // TODO: Remove the `use` function in favor of `registerGenericStore` registry = createRegistry() .use( ( originalRegistry ) => { originalRegisterStore = jest.spyOn( originalRegistry, 'registerStore' ); @@ -33,6 +34,9 @@ describe( 'persistence', () => { const options = Object.freeze( { persist: true, reducer() {} } ); registry.registerStore( 'test', options ); + + // use is a deprecated function and will produce a warning. + expect( console ).toHaveWarned(); } ); it( 'override values passed to registerStore', () => { diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index f242f28e2333f..693a72cf61498 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -6,6 +6,11 @@ import { mapValues, } from 'lodash'; +/** + * WordPress dependencies + */ +import deprecated from '@wordpress/deprecated'; + /** * Internal dependencies */ @@ -17,15 +22,11 @@ import dataStore from './store'; * * @typedef {WPDataRegistry} * - * @property {Function} registerReducer - * @property {Function} registerSelectors - * @property {Function} registerResolvers - * @property {Function} registerActions + * @property {Function} registerGenericStore * @property {Function} registerStore * @property {Function} subscribe * @property {Function} select * @property {Function} dispatch - * @property {Function} use */ /** @@ -94,16 +95,10 @@ export function createRegistry( storeConfigs = {} ) { return store && store.getActions(); } - /** - * Maps an object of function values to proxy invocation through to the - * current internal representation of the registry, which may be enhanced - * by plugins. - * TODO: Consider deprecating and removing this after plugins are no longer needed. - * - * @param {Object} attributes Object of function values. - * - * @return {Object} Object enhanced with plugin proxying. - */ + // + // Deprecated + // TODO: Remove this after `use()` is removed. + // function withPlugins( attributes ) { return mapValues( attributes, ( attribute, key ) => { if ( typeof attribute !== 'function' ) { @@ -145,61 +140,59 @@ export function createRegistry( storeConfigs = {} ) { use, }; - /** - * Registers a new sub-reducer to the global state and returns a Redux-like - * store object. - * TODO: Deprecate and remove this function in favor of registerStore - * - * @param {string} reducerKey Reducer key. - * @param {Object} reducer Reducer function. - * - * @return {Object} Store Object. - */ + // + // Deprecated + // registry.registerReducer = ( reducerKey, reducer ) => { + deprecated( 'registry.registerReducer', { + alternative: 'registry.registerStore', + plugin: 'Gutenberg', + version: '3.1.0', + } ); + const namespace = createNamespace( reducerKey, { reducer }, registry ); registerGenericStore( reducerKey, namespace ); return namespace.store; }; - /** - * Registers actions for external usage. - * TODO: Deprecate and remove this function in favor of registerStore - * - * @param {string} reducerKey Part of the state shape to register the - * selectors for. - * @param {Object} actions Actions to register. - */ + // + // Deprecated + // registry.registerActions = ( reducerKey, actions ) => { + deprecated( 'registry.registerActions', { + alternative: 'registry.registerStore', + plugin: 'Gutenberg', + version: '3.1.0', + } ); + const namespace = createNamespace( reducerKey, { actions }, registry ); registerGenericStore( reducerKey, namespace ); }; - /** - * Registers selectors for external usage. - * TODO: Deprecate and remove this function in favor of registerStore - * - * @param {string} reducerKey Part of the state shape to register the - * selectors for. - * @param {Object} selectors Selectors to register. Keys will be used as the - * public facing API. Selectors will get passed the - * state as first argument. - */ + // + // Deprecated + // registry.registerSelectors = ( reducerKey, selectors ) => { + deprecated( 'registry.registerSelectors', { + alternative: 'registry.registerStore', + plugin: 'Gutenberg', + version: '3.1.0', + } ); + const namespace = createNamespace( reducerKey, { selectors }, registry ); registerGenericStore( reducerKey, namespace ); }; - /** - * Registers resolvers for a given reducer key. Resolvers are side effects - * invoked once per argument set of a given selector call, used in ensuring - * that the data needs for the selector are satisfied. - * TODO: Deprecate and remove this function in favor of registerStore - * - * @param {string} reducerKey Part of the state shape to register the - * resolvers for. - * @param {Object} resolvers Resolvers to register. - */ + // + // Deprecated + // registry.registerResolvers = ( reducerKey, resolvers ) => { + deprecated( 'registry.registerResolvers', { + alternative: 'registry.registerStore', + plugin: 'Gutenberg', + version: '3.1.0', + } ); + const namespace = createNamespace( reducerKey, { resolvers }, registry ); registerGenericStore( reducerKey, namespace ); }; @@ -222,17 +215,16 @@ export function createRegistry( storeConfigs = {} ) { return namespace.store; }; - /** - * Enhances the registry with the prescribed set of overrides. Returns the - * enhanced registry to enable plugin chaining. - * TODO: Consider deprecating and removing this after plugins are no longer needed. - * - * @param {WPDataPlugin} plugin Plugin by which to enhance. - * @param {?Object} options Optional options to pass to plugin. - * - * @return {WPDataRegistry} Enhanced registry. - */ + // + // Deprecated + // function use( plugin, options ) { + deprecated( 'registry.use', { + alternative: 'registry.registerGenericStore', + plugin: 'Gutenberg', + version: '3.1.0', + } ); + registry = { ...registry, ...plugin( registry, options ), diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index 2663905e359ca..3d07e1d3b1e87 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -180,6 +180,7 @@ describe( 'createRegistry', () => { } ); } ); + // TODO: Refactor this into registerStore tests after this function is removed. describe( 'registerReducer', () => { it( 'Should append reducers to the state', () => { const reducer1 = () => 'chicken'; @@ -190,9 +191,13 @@ describe( 'createRegistry', () => { const store2 = registry.registerReducer( 'red2', reducer2 ); expect( store2.getState() ).toEqual( 'ribs' ); + + // This uses deprecated functions and will produce a warning. + expect( console ).toHaveWarned(); } ); } ); + // TODO: Refactor this into registerStore tests after this function is removed. describe( 'registerResolvers', () => { it( 'should not do anything for selectors which do not have resolvers', () => { registry.registerReducer( 'demo', ( state = 'OK' ) => state ); @@ -202,6 +207,9 @@ describe( 'createRegistry', () => { registry.registerResolvers( 'demo', {} ); expect( registry.select( 'demo' ).getValue() ).toBe( 'OK' ); + + // This uses deprecated functions and will produce a warning. + expect( console ).toHaveWarned(); } ); it( 'should behave as a side effect for the given selector, with arguments', () => { @@ -516,6 +524,9 @@ describe( 'createRegistry', () => { registry.dispatch( 'counter' ).increment(); // state = 1 registry.dispatch( 'counter' ).increment( 4 ); // state = 5 expect( store.getState() ).toBe( 5 ); + + // This uses deprecated functions and will produce a warning. + expect( console ).toHaveWarned(); } ); } ); @@ -550,6 +561,9 @@ describe( 'createRegistry', () => { registry.use( plugin, expectedOptions ); expect( actualOptions ).toBe( expectedOptions ); + + // This uses deprecated functions and will produce a warning. + expect( console ).toHaveWarned(); } ); it( 'should override base method', () => { From 407d50302e42e4378ad3adda583f9a038126ecba Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Thu, 1 Nov 2018 14:07:34 -0500 Subject: [PATCH 12/14] data: docs/changelog updates for deprecations This documents the deprecated.md and CHANGELOG.md files with information about th wp.data registry deprecations. It also sets the correct version for the deprecated calls to the next minor plugin version. --- docs/reference/deprecated.md | 5 +++++ packages/data/CHANGELOG.md | 14 ++++++++++++++ packages/data/src/registry.js | 10 +++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md index 252b56c2ef809..cd42e610efd9e 100644 --- a/docs/reference/deprecated.md +++ b/docs/reference/deprecated.md @@ -7,6 +7,11 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo - The following editor store actions have been removed: `createNotice`, `removeNotice`, `createSuccessNotice`, `createInfoNotice`, `createErrorNotice`, `createWarningNotice`. Use the equivalent actions by the same name from the `@wordpress/notices` module. - The id prop of wp.nux.DotTip has been removed. Please use the tipId prop instead. - `wp.blocks.isValidBlock` has been removed. Please use `wp.blocks.isValidBlockContent` instead but keep in mind that the order of params has changed. +- `wp.data` `registry.registerReducer` has been deprecated. Use `registry.registerStore` instead. +- `wp.data` `registry.registerSelectors` has been deprecated. Use `registry.registerStore` instead. +- `wp.data` `registry.registerActions` has been deprecated. Use `registry.registerStore` instead. +- `wp.data` `registry.registerResolvers` has been deprecated. Use `registry.registerStore` instead. +- `wp.data` `registry.use` has been deprecated. Implement with `registry.registerGenericStore` instead. ## 4.3.0 diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 34ed8c5a73bb3..1af3c18b8c994 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -1,3 +1,17 @@ +## 3.1.0 (Unreleased) + +### New Features + +- `registry.registerGenericStore` has been added to support integration with existing data systems. + +### Deprecations + +- `registry.registerReducer` has been deprecated. Use `registry.registerStore` instead. +- `registry.registerSelectors` has been deprecated. Use `registry.registerStore` instead. +- `registry.registerActions` has been deprecated. Use `registry.registerStore` instead. +- `registry.registerResolvers` has been deprecated. Use `registry.registerStore` instead. +- `registry.use` has been deprecated. Implement with `registry.registerGenericStore` instead. + ## 3.0.1 (2018-10-30) ### Internal diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index 693a72cf61498..1ae748fc54bb3 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -147,7 +147,7 @@ export function createRegistry( storeConfigs = {} ) { deprecated( 'registry.registerReducer', { alternative: 'registry.registerStore', plugin: 'Gutenberg', - version: '3.1.0', + version: '4.4.0', } ); const namespace = createNamespace( reducerKey, { reducer }, registry ); @@ -162,7 +162,7 @@ export function createRegistry( storeConfigs = {} ) { deprecated( 'registry.registerActions', { alternative: 'registry.registerStore', plugin: 'Gutenberg', - version: '3.1.0', + version: '4.4.0', } ); const namespace = createNamespace( reducerKey, { actions }, registry ); @@ -176,7 +176,7 @@ export function createRegistry( storeConfigs = {} ) { deprecated( 'registry.registerSelectors', { alternative: 'registry.registerStore', plugin: 'Gutenberg', - version: '3.1.0', + version: '4.4.0', } ); const namespace = createNamespace( reducerKey, { selectors }, registry ); @@ -190,7 +190,7 @@ export function createRegistry( storeConfigs = {} ) { deprecated( 'registry.registerResolvers', { alternative: 'registry.registerStore', plugin: 'Gutenberg', - version: '3.1.0', + version: '4.4.0', } ); const namespace = createNamespace( reducerKey, { resolvers }, registry ); @@ -222,7 +222,7 @@ export function createRegistry( storeConfigs = {} ) { deprecated( 'registry.use', { alternative: 'registry.registerGenericStore', plugin: 'Gutenberg', - version: '3.1.0', + version: '4.4.0', } ); registry = { From f95b7bc36465cfc2915a8e832e310062c23f7574 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Thu, 1 Nov 2018 14:16:47 -0500 Subject: [PATCH 13/14] data: un-deprecate `registry.use` This removes the deprecation of `use` because it's internally referenced and breaks unit tests. The deprecation of `use` should be done in a future PR that changes the behavior to not call `use`. --- docs/reference/deprecated.md | 1 - packages/data/CHANGELOG.md | 1 - packages/data/src/plugins/persistence/test/index.js | 3 --- packages/data/src/registry.js | 9 ++------- packages/data/src/test/registry.js | 3 --- 5 files changed, 2 insertions(+), 15 deletions(-) diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md index cd42e610efd9e..0bf9117f287c3 100644 --- a/docs/reference/deprecated.md +++ b/docs/reference/deprecated.md @@ -11,7 +11,6 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo - `wp.data` `registry.registerSelectors` has been deprecated. Use `registry.registerStore` instead. - `wp.data` `registry.registerActions` has been deprecated. Use `registry.registerStore` instead. - `wp.data` `registry.registerResolvers` has been deprecated. Use `registry.registerStore` instead. -- `wp.data` `registry.use` has been deprecated. Implement with `registry.registerGenericStore` instead. ## 4.3.0 diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 1af3c18b8c994..8d392d6adf171 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -10,7 +10,6 @@ - `registry.registerSelectors` has been deprecated. Use `registry.registerStore` instead. - `registry.registerActions` has been deprecated. Use `registry.registerStore` instead. - `registry.registerResolvers` has been deprecated. Use `registry.registerStore` instead. -- `registry.use` has been deprecated. Implement with `registry.registerGenericStore` instead. ## 3.0.1 (2018-10-30) diff --git a/packages/data/src/plugins/persistence/test/index.js b/packages/data/src/plugins/persistence/test/index.js index f352bd84ae7cb..fc04e395cb4a2 100644 --- a/packages/data/src/plugins/persistence/test/index.js +++ b/packages/data/src/plugins/persistence/test/index.js @@ -34,9 +34,6 @@ describe( 'persistence', () => { const options = Object.freeze( { persist: true, reducer() {} } ); registry.registerStore( 'test', options ); - - // use is a deprecated function and will produce a warning. - expect( console ).toHaveWarned(); } ); it( 'override values passed to registerStore', () => { diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index 1ae748fc54bb3..fc9724ca5358b 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -216,15 +216,10 @@ export function createRegistry( storeConfigs = {} ) { }; // - // Deprecated + // TODO: + // This function will be deprecated as soon as it is no longer internally referenced. // function use( plugin, options ) { - deprecated( 'registry.use', { - alternative: 'registry.registerGenericStore', - plugin: 'Gutenberg', - version: '4.4.0', - } ); - registry = { ...registry, ...plugin( registry, options ), diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index 3d07e1d3b1e87..f0d483a9a1663 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -561,9 +561,6 @@ describe( 'createRegistry', () => { registry.use( plugin, expectedOptions ); expect( actualOptions ).toBe( expectedOptions ); - - // This uses deprecated functions and will produce a warning. - expect( console ).toHaveWarned(); } ); it( 'should override base method', () => { From d0c6dbad1e002d2e189e1e030abb066096553811 Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Fri, 2 Nov 2018 13:55:38 -0500 Subject: [PATCH 14/14] data: Add deprected as script dependency to data This adds the `wp-deprecated` script as a js script dependency to `wp-data`. --- lib/client-assets.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/client-assets.php b/lib/client-assets.php index b68c2fa5c9903..3f462c030bf21 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -299,6 +299,7 @@ function gutenberg_register_scripts_and_styles() { array( 'lodash', 'wp-compose', + 'wp-deprecated', 'wp-element', 'wp-is-shallow-equal', 'wp-polyfill',