Skip to content

Commit 1e8d781

Browse files
authored
Refactor the core/data store to be independent from the registry (#14634)
1 parent d9768ad commit 1e8d781

File tree

19 files changed

+220
-251
lines changed

19 files changed

+220
-251
lines changed

lib/client-assets.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ function gutenberg_register_scripts_and_styles() {
245245
);
246246

247247
// TEMPORARY: Core does not (yet) provide persistence migration from the
248-
// introduction of the block editor.
248+
// introduction of the block editor and still calls the data plugins.
249+
// We unset the existing inline scripts first.
250+
$wp_scripts->registered['wp-data']->extra['after'] = array();
249251
wp_add_inline_script(
250252
'wp-data',
251253
implode(
@@ -254,8 +256,10 @@ function gutenberg_register_scripts_and_styles() {
254256
'( function() {',
255257
' var userId = ' . get_current_user_ID() . ';',
256258
' var storageKey = "WP_DATA_USER_" + userId;',
259+
' wp.data',
260+
' .use( wp.data.plugins.persistence, { storageKey: storageKey } );',
257261
' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );',
258-
'} )()',
262+
'} )();',
259263
)
260264
)
261265
);

lib/packages-dependencies.php

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
'wp-data' => array(
112112
'lodash',
113113
'wp-compose',
114+
'wp-deprecated',
114115
'wp-element',
115116
'wp-is-shallow-equal',
116117
'wp-priority-queue',

package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/data/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"dependencies": {
2525
"@babel/runtime": "^7.3.1",
2626
"@wordpress/compose": "file:../compose",
27+
"@wordpress/deprecated": "file:../deprecated",
2728
"@wordpress/element": "file:../element",
2829
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
2930
"@wordpress/priority-queue": "file:../priority-queue",

packages/data/src/namespace-store.js packages/data/src/namespace-store/index.js

+80-54
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ import {
77
get,
88
mapValues,
99
} from 'lodash';
10+
import combineReducers from 'turbo-combine-reducers';
11+
12+
/**
13+
* WordPress dependencies
14+
*/
15+
import createReduxRoutineMiddleware from '@wordpress/redux-routine';
1016

1117
/**
1218
* Internal dependencies
1319
*/
14-
import promise from './promise-middleware';
15-
import createResolversCacheMiddleware from './resolvers-cache-middleware';
20+
import promise from '../promise-middleware';
21+
import createResolversCacheMiddleware from '../resolvers-cache-middleware';
22+
import metadataReducer from './metadata/reducer';
23+
import * as metadataSelectors from './metadata/selectors';
24+
import * as metadataActions from './metadata/actions';
1625

1726
/**
1827
* Creates a namespace object with a store derived from the reducer given.
@@ -27,29 +36,46 @@ export default function createNamespace( key, options, registry ) {
2736
const reducer = options.reducer;
2837
const store = createReduxStore( key, options, registry );
2938

30-
let selectors, actions, resolvers;
31-
if ( options.actions ) {
32-
actions = mapActions( options.actions, store );
33-
}
34-
if ( options.selectors ) {
35-
selectors = mapSelectors( options.selectors, store, registry );
36-
}
39+
let resolvers;
40+
const actions = mapActions( {
41+
...metadataActions,
42+
...options.actions,
43+
}, store );
44+
let selectors = mapSelectors( {
45+
...mapValues( metadataSelectors, ( selector ) => ( state, ...args ) => selector( state.metadata, ...args ) ),
46+
...mapValues( options.selectors, ( selector ) => {
47+
if ( selector.isRegistrySelector ) {
48+
const mappedSelector = ( reg ) => ( state, ...args ) => {
49+
return selector( reg )( state.root, ...args );
50+
};
51+
mappedSelector.isRegistrySelector = selector.isRegistrySelector;
52+
return mappedSelector;
53+
}
54+
55+
return ( state, ...args ) => selector( state.root, ...args );
56+
} ),
57+
}, store, registry );
3758
if ( options.resolvers ) {
38-
const fulfillment = getCoreDataFulfillment( registry, key );
39-
const result = mapResolvers( options.resolvers, selectors, fulfillment, store );
59+
const result = mapResolvers( options.resolvers, selectors, store );
4060
resolvers = result.resolvers;
4161
selectors = result.selectors;
4262
}
4363

4464
const getSelectors = () => selectors;
4565
const getActions = () => actions;
4666

67+
// We have some modules monkey-patching the store object
68+
// It's wrong to do so but until we refactor all of our effects to controls
69+
// We need to keep the same "store" instance here.
70+
store.__unstableOriginalGetState = store.getState;
71+
store.getState = () => store.__unstableOriginalGetState().root;
72+
4773
// Customize subscribe behavior to call listeners only on effective change,
4874
// not on every dispatch.
4975
const subscribe = store && function( listener ) {
50-
let lastState = store.getState();
76+
let lastState = store.__unstableOriginalGetState();
5177
store.subscribe( () => {
52-
const state = store.getState();
78+
const state = store.__unstableOriginalGetState();
5379
const hasChanged = state !== lastState;
5480
lastState = state;
5581

@@ -84,15 +110,36 @@ export default function createNamespace( key, options, registry ) {
84110
* @return {Object} Newly created redux store.
85111
*/
86112
function createReduxStore( key, options, registry ) {
113+
const middlewares = [
114+
createResolversCacheMiddleware( registry, key ),
115+
promise,
116+
];
117+
118+
if ( options.controls ) {
119+
const normalizedControls = mapValues( options.controls, ( control ) => {
120+
return control.isRegistryControl ? control( registry ) : control;
121+
} );
122+
middlewares.push( createReduxRoutineMiddleware( normalizedControls ) );
123+
}
124+
87125
const enhancers = [
88-
applyMiddleware( createResolversCacheMiddleware( registry, key ), promise ),
126+
applyMiddleware( ...middlewares ),
89127
];
90128
if ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ) {
91129
enhancers.push( window.__REDUX_DEVTOOLS_EXTENSION__( { name: key, instanceId: key } ) );
92130
}
93131

94132
const { reducer, initialState } = options;
95-
return createStore( reducer, initialState, flowRight( enhancers ) );
133+
const enhancedReducer = combineReducers( {
134+
metadata: metadataReducer,
135+
root: reducer,
136+
} );
137+
138+
return createStore(
139+
enhancedReducer,
140+
{ root: initialState },
141+
flowRight( enhancers )
142+
);
96143
}
97144

98145
/**
@@ -120,7 +167,7 @@ function mapSelectors( selectors, store, registry ) {
120167
// direct assignment.
121168
const argsLength = arguments.length;
122169
const args = new Array( argsLength + 1 );
123-
args[ 0 ] = store.getState();
170+
args[ 0 ] = store.__unstableOriginalGetState();
124171
for ( let i = 0; i < argsLength; i++ ) {
125172
args[ i + 1 ] = arguments[ i ];
126173
}
@@ -151,11 +198,15 @@ function mapActions( actions, store ) {
151198
*
152199
* @param {Object} resolvers Resolvers to register.
153200
* @param {Object} selectors The current selectors to be modified.
154-
* @param {Object} fulfillment Fulfillment implementation functions.
155201
* @param {Object} store The redux store to which the resolvers should be mapped.
156202
* @return {Object} An object containing updated selectors and resolvers.
157203
*/
158-
function mapResolvers( resolvers, selectors, fulfillment, store ) {
204+
function mapResolvers( resolvers, selectors, store ) {
205+
const mappedResolvers = mapValues( resolvers, ( resolver ) => {
206+
const { fulfill: resolverFulfill = resolver } = resolver;
207+
return { ...resolver, fulfill: resolverFulfill };
208+
} );
209+
159210
const mapSelector = ( selector, selectorName ) => {
160211
const resolver = resolvers[ selectorName ];
161212
if ( ! resolver ) {
@@ -169,68 +220,43 @@ function mapResolvers( resolvers, selectors, fulfillment, store ) {
169220
return;
170221
}
171222

172-
if ( fulfillment.hasStarted( selectorName, args ) ) {
223+
const { metadata } = store.__unstableOriginalGetState();
224+
if ( metadataSelectors.hasStartedResolution( metadata, selectorName, args ) ) {
173225
return;
174226
}
175227

176-
fulfillment.start( selectorName, args );
177-
await fulfillment.fulfill( selectorName, ...args );
178-
fulfillment.finish( selectorName, args );
228+
store.dispatch( metadataActions.startResolution( selectorName, args ) );
229+
await fulfillResolver( store, mappedResolvers, selectorName, ...args );
230+
store.dispatch( metadataActions.finishResolution( selectorName, args ) );
179231
}
180232

181233
fulfillSelector( ...args );
182234
return selector( ...args );
183235
};
184236
};
185237

186-
const mappedResolvers = mapValues( resolvers, ( resolver ) => {
187-
const { fulfill: resolverFulfill = resolver } = resolver;
188-
return { ...resolver, fulfill: resolverFulfill };
189-
} );
190-
191238
return {
192239
resolvers: mappedResolvers,
193240
selectors: mapValues( selectors, mapSelector ),
194241
};
195242
}
196243

197-
/**
198-
* Bundles up fulfillment functions for resolvers.
199-
* @param {Object} registry Registry reference, for fulfilling via resolvers
200-
* @param {string} key Part of the state shape to register the
201-
* selectors for.
202-
* @return {Object} An object providing fulfillment functions.
203-
*/
204-
function getCoreDataFulfillment( registry, key ) {
205-
const { hasStartedResolution } = registry.select( 'core/data' );
206-
const { startResolution, finishResolution } = registry.dispatch( 'core/data' );
207-
208-
return {
209-
hasStarted: ( ...args ) => hasStartedResolution( key, ...args ),
210-
start: ( ...args ) => startResolution( key, ...args ),
211-
finish: ( ...args ) => finishResolution( key, ...args ),
212-
fulfill: ( ...args ) => fulfillWithRegistry( registry, key, ...args ),
213-
};
214-
}
215-
216244
/**
217245
* Calls a resolver given arguments
218246
*
219-
* @param {Object} registry Registry reference, for fulfilling via resolvers
220-
* @param {string} key Part of the state shape to register the
221-
* selectors for.
247+
* @param {Object} store Store reference, for fulfilling via resolvers
248+
* @param {Object} resolvers Store Resolvers
222249
* @param {string} selectorName Selector name to fulfill.
223-
* @param {Array} args Selector Arguments.
250+
* @param {Array} args Selector Arguments.
224251
*/
225-
async function fulfillWithRegistry( registry, key, selectorName, ...args ) {
226-
const namespace = registry.stores[ key ];
227-
const resolver = get( namespace, [ 'resolvers', selectorName ] );
252+
async function fulfillResolver( store, resolvers, selectorName, ...args ) {
253+
const resolver = get( resolvers, [ selectorName ] );
228254
if ( ! resolver ) {
229255
return;
230256
}
231257

232258
const action = resolver.fulfill( ...args );
233259
if ( action ) {
234-
await namespace.store.dispatch( action );
260+
await store.dispatch( action );
235261
}
236262
}

packages/data/src/store/actions.js packages/data/src/namespace-store/metadata/actions.js

+8-22
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
* Returns an action object used in signalling that selector resolution has
33
* started.
44
*
5-
* @param {string} reducerKey Registered store reducer key.
65
* @param {string} selectorName Name of selector for which resolver triggered.
76
* @param {...*} args Arguments to associate for uniqueness.
87
*
98
* @return {Object} Action object.
109
*/
11-
export function startResolution( reducerKey, selectorName, args ) {
10+
export function startResolution( selectorName, args ) {
1211
return {
1312
type: 'START_RESOLUTION',
14-
reducerKey,
1513
selectorName,
1614
args,
1715
};
@@ -21,16 +19,14 @@ export function startResolution( reducerKey, selectorName, args ) {
2119
* Returns an action object used in signalling that selector resolution has
2220
* completed.
2321
*
24-
* @param {string} reducerKey Registered store reducer key.
2522
* @param {string} selectorName Name of selector for which resolver triggered.
2623
* @param {...*} args Arguments to associate for uniqueness.
2724
*
2825
* @return {Object} Action object.
2926
*/
30-
export function finishResolution( reducerKey, selectorName, args ) {
27+
export function finishResolution( selectorName, args ) {
3128
return {
3229
type: 'FINISH_RESOLUTION',
33-
reducerKey,
3430
selectorName,
3531
args,
3632
};
@@ -39,53 +35,43 @@ export function finishResolution( reducerKey, selectorName, args ) {
3935
/**
4036
* Returns an action object used in signalling that we should invalidate the resolution cache.
4137
*
42-
* @param {string} reducerKey Registered store reducer key.
4338
* @param {string} selectorName Name of selector for which resolver should be invalidated.
4439
* @param {Array} args Arguments to associate for uniqueness.
4540
*
4641
* @return {Object} Action object.
4742
*/
48-
export function invalidateResolution( reducerKey, selectorName, args ) {
43+
export function invalidateResolution( selectorName, args ) {
4944
return {
5045
type: 'INVALIDATE_RESOLUTION',
51-
reducerKey,
5246
selectorName,
5347
args,
5448
};
5549
}
5650

5751
/**
58-
* Returns an action object used in signalling that the resolution cache for a
59-
* given reducerKey should be invalidated.
60-
*
61-
* @param {string} reducerKey Registered store reducer key.
52+
* Returns an action object used in signalling that the resolution
53+
* should be invalidated.
6254
*
6355
* @return {Object} Action object.
6456
*/
65-
export function invalidateResolutionForStore( reducerKey ) {
57+
export function invalidateResolutionForStore() {
6658
return {
6759
type: 'INVALIDATE_RESOLUTION_FOR_STORE',
68-
reducerKey,
6960
};
7061
}
7162

7263
/**
7364
* Returns an action object used in signalling that the resolution cache for a
74-
* given reducerKey and selectorName should be invalidated.
65+
* given selectorName should be invalidated.
7566
*
76-
* @param {string} reducerKey Registered store reducer key.
7767
* @param {string} selectorName Name of selector for which all resolvers should
7868
* be invalidated.
7969
*
8070
* @return {Object} Action object.
8171
*/
82-
export function invalidateResolutionForStoreSelector(
83-
reducerKey,
84-
selectorName
85-
) {
72+
export function invalidateResolutionForStoreSelector( selectorName ) {
8673
return {
8774
type: 'INVALIDATE_RESOLUTION_FOR_STORE_SELECTOR',
88-
reducerKey,
8975
selectorName,
9076
};
9177
}

0 commit comments

Comments
 (0)