Skip to content

Commit

Permalink
Data Module: Restrict the state access to the module registering the …
Browse files Browse the repository at this point in the history
…reducer only
  • Loading branch information
youknowriad committed Dec 18, 2017
1 parent 5c33f6f commit 7b6964a
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 41 deletions.
13 changes: 6 additions & 7 deletions data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ This module holds a global state variable and exposes a "Redux-like" API contain

If your module or plugin needs to store and manipulate client-side data, you'll have to register a "reducer" to do so. A reducer is a function taking the previous `state` and `action` and returns an update `state`. You can learn more about reducers on the [Redux Documentation](https://redux.js.org/docs/basics/Reducers.html)

### `wp.data.getState()`
This function returns a Redux-like store object with the following methods:

A simple function to returns the JS object containing the state of all the WP Modules.
This function is present for convenience to use things like `react-redux`.
You should not use rely on other modules state since the state object's shape may change over time breaking your module.
An official way to expose your module's state will be added later.
#### `store.getState()`

### `wp.data.subscribe( listener: function )`
Returns the state object of the registered reducer.

#### `store.subscribe( listener: function )`

Registers a `listener` function called everytime the state is updated.

### `wp.data.dispatch( action: object )`
#### `store.dispatch( action: object )`

The dispatch function should be called to trigger the registered reducers function and update the state. An `action` object should be passed to this action. This action is passed to the registered reducers in addition to the previous state.
31 changes: 22 additions & 9 deletions data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { createStore, combineReducers } from 'redux';
import { flowRight } from 'lodash';
import { flowRight, get } from 'lodash';

/**
* Module constants
Expand All @@ -17,18 +17,31 @@ const initialReducer = () => ( {} );
const store = createStore( initialReducer, {}, flowRight( enhancers ) );

/**
* Registers a new sub reducer to the global state
* Registers a new sub reducer to the global state and returns a Redux-like store object.
*
* @param {String} key Reducer key
* @param {Object} reducer Reducer function
* @param {String} key Reducer key
* @param {Object} reducer Reducer function
*
* @return {Object} Store Object
*/
export function registerReducer( key, reducer ) {
reducers[ key ] = reducer;
store.replaceReducer( combineReducers( reducers ) );
}

export const subscribe = store.subscribe;
return {
dispatch: store.dispatch,
getState: () => get( store.getState(), key ),
subscribe( listener ) {
let previousState = get( store.getState(), key );
const unsubscribe = store.subscribe( () => {
const newState = get( store.getState(), key );
if ( newState !== previousState ) {
listener();
previousState = newState;
}
} );

export const dispatch = store.dispatch;

export const getState = store.getState;
return unsubscribe;
},
};
}
13 changes: 5 additions & 8 deletions data/test/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { registerReducer, getState } from '../';
import { registerReducer } from '../';

describe( 'store', () => {
it( 'Should append reducers to the state', () => {
const reducer1 = () => 'chicken';
const reducer2 = () => 'ribs';

registerReducer( 'red1', reducer1 );
expect( getState() ).toEqual( { red1: 'chicken' } );
const store = registerReducer( 'red1', reducer1 );
expect( store.getState() ).toEqual( 'chicken' );

registerReducer( 'red2', reducer2 );
expect( getState() ).toEqual( {
red1: 'chicken',
red2: 'ribs',
} );
const store2 = registerReducer( 'red2', reducer2 );
expect( store2.getState() ).toEqual( 'ribs' );
} );
} );
5 changes: 3 additions & 2 deletions editor/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { PREFERENCES_DEFAULTS } from './defaults';
import reducer from './reducer';
import { withRehydratation, loadAndPersist } from './persist';
import enhanceWithBrowserSize from './browser';
import store from './store';
import applyMiddlewares from './middlewares';

/**
* Module Constants
*/
const STORAGE_KEY = `GUTENBERG_PREFERENCES_${ window.userSettings.uid }`;

registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) );
let store = registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) );
store = applyMiddlewares( store );
loadAndPersist( store, 'preferences', STORAGE_KEY, PREFERENCES_DEFAULTS );
enhanceWithBrowserSize( store );

Expand Down
23 changes: 8 additions & 15 deletions editor/store/store.js → editor/store/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
*/
import refx from 'refx';
import multi from 'redux-multi';
import { get, flowRight } from 'lodash';

/**
* WordPress dependencies
*/
import { dispatch, getState, subscribe } from '@wordpress/data';
import { flowRight } from 'lodash';

/**
* Internal dependencies
Expand All @@ -19,18 +14,17 @@ import effects from './effects';
/**
* Applies the custom middlewares used specifically in the editor module
*
* It also restricts the getState call to the module's partial state only
* @param {Object} store Store Object
*
* @return {Object} Redux custom store
* @return {Object} Update Store Object
*/
function applyMiddlewaresAndRestrictState() {
function applyMiddlewares( store ) {
const middlewares = [
mobileMiddleware,
refx( effects ),
multi,
];

const enhancedGetState = () => get( getState(), 'core/editor' );
let enhancedDispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
Expand All @@ -40,17 +34,16 @@ function applyMiddlewaresAndRestrictState() {
let chain = [];

const middlewareAPI = {
getState: enhancedGetState,
getState: store.getState,
dispatch: ( ...args ) => enhancedDispatch( ...args ),
};
chain = middlewares.map( middleware => middleware( middlewareAPI ) );
enhancedDispatch = flowRight( ...chain )( dispatch );
enhancedDispatch = flowRight( ...chain )( store.dispatch );

return {
...store,
dispatch: enhancedDispatch,
getState: enhancedGetState,
subscribe,
};
}

export default applyMiddlewaresAndRestrictState();
export default applyMiddlewares;

0 comments on commit 7b6964a

Please sign in to comment.