Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK: Try wp.data with calypso state tree #26838

Closed
33 changes: 20 additions & 13 deletions client/blocks/color-scheme-picker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
*/
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { translate } from 'i18n-calypso';
import { compose } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import QueryPreferences from 'components/data/query-preferences';
import { savePreference, setPreference } from 'state/preferences/actions';
import { getPreference } from 'state/preferences/selectors';
import getColorSchemesData from './constants';
import FormRadiosBar from 'components/forms/form-radios-bar';

Expand Down Expand Up @@ -49,16 +48,24 @@ class ColorSchemePicker extends PureComponent {
}
}

const saveColorSchemePreference = ( preference, temporarySelection ) =>
temporarySelection
? setPreference( 'colorScheme', preference )
: savePreference( 'colorScheme', preference );
export default compose( [
withSelect( select => {
const { getPreference } = select( 'calypso/preferences' );
return {
colorSchemePreference: getPreference( 'colorScheme' ),
};
} ),
withDispatch( dispatch => {
const { setPreference, savePreference } = dispatch( 'calypso/preferences' );

export default connect(
state => {
return {
colorSchemePreference: getPreference( state, 'colorScheme' ),
saveColorSchemePreference( preference, temporarySelection ) {
if ( temporarySelection ) {
setPreference( 'colorScheme', preference );
} else {
savePreference( 'colorScheme', preference );
}
},
};
},
{ saveColorSchemePreference }
)( ColorSchemePicker );
} ),
] )( ColorSchemePicker );
9 changes: 8 additions & 1 deletion client/components/root-child/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ReactDom from 'react-dom';
import PropTypes from 'prop-types';
import React from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { CalypsoWPDataProvider } from 'state/wp-data';

export default class RootChild extends React.Component {
static contextTypes = {
Expand Down Expand Up @@ -46,7 +47,13 @@ export default class RootChild extends React.Component {
// Context is lost when creating a new render hierarchy, so ensure that
// we preserve the context that we care about
if ( this.context.store ) {
content = <ReduxProvider store={ this.context.store }>{ content }</ReduxProvider>;
content = (
<ReduxProvider store={ this.context.store }>
<CalypsoWPDataProvider calypsoStore={ this.context.store }>
{ content }
</CalypsoWPDataProvider>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to go to client/layout instead, at least for the purpose of this PR. RootChild is mostly used by modals, whereas the color scheme picker (like most components) is a direct descendant of client/layout. I think that in this PR, withSelect() and withDispatch() are currently picking up a 'global' registry instance (inside @wp/data).

I think I got this to work so it picks up the correct registry in my PR, #26930.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip. I'll adjust this one too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have the WP Data Provider in both places? Both here and in ReduxWrappedLayout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in e9be45c

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have the WP Data Provider in both places? Both here and in ReduxWrappedLayout?

Eventually, we'll need that Provider at the top of all component hierarchies contain components wrapped in withSelect and/or withDispatch, but for the sake of this PR, wrapping only in ReduxWrappedLayout should be enough.

(We might end up writing a HOC that contains both the Redux and Calypso WP Data Provider, and wrap all component hierarchies in that.)

</ReduxProvider>
);
}

ReactDom.render( content, this.container );
Expand Down
13 changes: 8 additions & 5 deletions client/controller/index.web.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import page from 'page';
/**
* Internal Dependencies
*/
import { CalypsoWPDataProvider } from 'state/wp-data';
import config from 'config';
import Layout from 'layout';
import LayoutLoggedOut from 'layout/logged-out';
Expand All @@ -29,11 +30,13 @@ const user = userFactory();

export const ReduxWrappedLayout = ( { store, primary, secondary, redirectUri } ) => (
<ReduxProvider store={ store }>
{ getCurrentUser( store.getState() ) ? (
<Layout primary={ primary } secondary={ secondary } user={ user } />
) : (
<LayoutLoggedOut primary={ primary } secondary={ secondary } redirectUri={ redirectUri } />
) }
<CalypsoWPDataProvider calypsoStore={ store }>
{ getCurrentUser( store.getState() ) ? (
<Layout primary={ primary } secondary={ secondary } user={ user } />
) : (
<LayoutLoggedOut primary={ primary } secondary={ secondary } redirectUri={ redirectUri } />
) }
</CalypsoWPDataProvider>
</ReduxProvider>
);

Expand Down
8 changes: 8 additions & 0 deletions client/state/wp-data/calypso-stores/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import preferences from './preferences';

export function registerStores( registry, calypsoStore ) {
registry.registerStore( 'calypso/preferences', preferences, calypsoStore );
};
19 changes: 19 additions & 0 deletions client/state/wp-data/calypso-stores/preferences.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** @format */

/**
* Internal dependencies
*/
import { fetchPreferences, setPreference, savePreference } from 'state/preferences/actions';
import { getPreference, isFetchingPreferences } from 'state/preferences/selectors';

export default {
selectors: {
getPreference,
isFetchingPreferences,
},
actions: {
fetchPreferences,
setPreference,
savePreference,
},
};
40 changes: 40 additions & 0 deletions client/state/wp-data/calypso-wp-data-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/** @format */
/**
* External dependencies
*/
import React, { Component } from 'react';
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
* Internal dependencies
*/
import internalsPlugin from './plugins/internals-plugin';
import customStorePlugin from './plugins/custom-store-plugin';
import { registerStores } from './calypso-stores';

class CalypsoWPDataProvider extends Component {
constructor( props ) {
super( ...arguments );
this.updateRegistry( props.calypsoStore );
}

updateRegistry( calypsoStore ) {
this.registry = createRegistry()
.use( internalsPlugin )
.use( customStorePlugin );

registerStores( this.registry, calypsoStore );
}

componentDidUpdate( prevProps ) {
if ( prevProps.calypsoStore !== this.props.calypsoStore ) {
this.updateRegistry( this.props.calypsoStore );
}
}

render() {
return <RegistryProvider value={ this.registry }>{ this.props.children }</RegistryProvider>;
}
}

export default CalypsoWPDataProvider;
5 changes: 5 additions & 0 deletions client/state/wp-data/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @format */
/**
* Internal dependencies
*/
export { default as CalypsoWPDataProvider } from './calypso-wp-data-provider';
88 changes: 88 additions & 0 deletions client/state/wp-data/plugins/custom-store-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/** @format */
/**
* External dependencies
*/
import { findKey } from 'lodash';

/**
* Creates a plugin that allows custom store implementations.
*
* IMPORTANT: This plugin depends on the `internals-plugin` to work.
* It must be given a registry already using that plugin.
*
* @param {Object} registry The original registry to wrap.
* @return {Object} The wrapped registry from this plugin.
*/
function customStorePlugin( registry ) {
if ( ! registry._internals ) {
throw TypeError( 'Expected a registry with _internals. See internals-plugin' );
}

/**
* Registers a custom store with actions and selectors.
*
* @param {string} reducerKey Reducer key.
* @param {Object} store Existing redux-like store to be used.
* @return {Object} Store originally given, now registered.
*/
function registerCustomStore( reducerKey, store ) {
const { globalListener, namespaces } = registry._internals;
const existingStoreKey = findKey( namespaces, value => value.store === store );

namespaces[ reducerKey ] = { store };

if ( ! existingStoreKey ) {
// 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;
}

/**
* Registers a store within the registry.
*
* @param {String} reducerKey The desired key to denote this store.
* @param {Object} options Options for store, such as actions, selectors.
* @param {Object} [customStore] Optional custom store to use instead of creating one.
* @return {Object} The newly registered store.
*/
function registerStore( reducerKey, options, customStore ) {
if ( customStore ) {
const store = registerCustomStore( reducerKey, customStore );

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;
}

// No store given in options, pass through to normal operation.
return registry.registerStore( reducerKey, options );
}

return {
registerStore,
};
}

export default customStorePlugin;
Loading