diff --git a/packages/data/README.md b/packages/data/README.md
index 83aeb72e4513ee..8984e86b1c5770 100644
--- a/packages/data/README.md
+++ b/packages/data/README.md
@@ -379,11 +379,43 @@ _Returns_
# **RegistryConsumer**
-Undocumented declaration.
+A custom react Context consumer exposing the provided `registry` to
+children components. Used along with the RegistryProvider.
+
+You can read more about the react context api here:
+
+
+_Usage_
+
+````js
+const {
+ RegistryProvider,
+ RegistryConsumer,
+ createRegistry
+} = wp.data;
+
+const registry = createRegistry( {} );
+
+const App = ( { props } ) => {
+ return
+ Hello There
+
+ { ( registry ) => (
+
+
+}
# **RegistryProvider**
-Undocumented declaration.
+A custom Context provider for exposing the provided `registry` to children
+components via a consumer.
+
+See RegistryConsumer documentation for
+example.
# **select**
@@ -391,13 +423,13 @@ Given the name of a registered store, returns an object of the store's selectors
The selector functions are been pre-bound to pass the current state automatically.
As a consumer, you need only pass arguments of the selector, if applicable.
-_Usage_
+*Usage*
```js
const { select } = wp.data;
select( 'my-shop' ).getPrice( 'hammer' );
-```
+````
_Parameters_
@@ -435,6 +467,91 @@ _Parameters_
Undocumented declaration.
+# **useRegistry**
+
+A custom react hook exposing the registry context for use.
+
+This exposes the `registry` value provided via the
+Registry Provider to a component implementing
+this hook.
+
+It acts similarly to the `useContext` react hook.
+
+Note: Generally speaking, `useRegistry` is a low level hook that in most cases
+won't be needed for implementation. Most interactions with the wp.data api
+can be performed via the `useSelect` hook, or the `withSelect` and
+`withDispatch` higher order components.
+
+_Usage_
+
+```js
+const {
+ RegistryProvider,
+ createRegistry,
+ useRegistry,
+} = wp.data
+
+const registry = createRegistry( {} );
+
+const SomeChildUsingRegistry = ( props ) => {
+ const registry = useRegistry( registry );
+ // ...logic implementing the registry in other react hooks.
+};
+
+
+const ParentProvidingRegistry = ( props ) => {
+ return
+
+
+};
+```
+
+_Returns_
+
+- `Function`: A custom react hook exposing the registry context value.
+
+# **useSelect**
+
+Custom react hook for retrieving props from registered selectors.
+
+In general, this custom React hook follows the
+[rules of hooks](https://reactjs.org/docs/hooks-rules.html).
+
+_Usage_
+
+```js
+const { useSelect } = wp.data;
+
+function HammerPriceDisplay( { currency } ) {
+ const price = useSelect( ( select ) => {
+ return select( 'my-shop' ).getPrice( 'hammer', currency )
+ }, [ currency ] );
+ return new Intl.NumberFormat( 'en-US', {
+ style: 'currency',
+ currency,
+ } ).format( price );
+}
+
+// Rendered in the application:
+//
+```
+
+In the above example, when `HammerPriceDisplay` is rendered into an
+application, the price will be retrieved from the store state using the
+`mapSelect` callback on `useSelect`. If the currency prop changes then
+any price in the state for that currency is retrieved. If the currency prop
+doesn't change and other props are passed in that do change, the price will
+not change because the dependency is just the currency.
+
+_Parameters_
+
+- _\_mapSelect_ `Function`: Function called on every state change. The returned value is exposed to the component implementing this hook. The function receives the `registry.select` method on the first argument and the `registry` on the second argument.
+- _deps_ `Array`: If provided, this memoizes the mapSelect so the same `mapSelect` is invoked on every state change unless the dependencies change.
+
+_Returns_
+
+- `Function`: A custom react hook.
+
# **withDispatch**
Higher-order component used to add dispatch props using registered action creators.
@@ -516,7 +633,47 @@ _Returns_
# **withSelect**
-Undocumented declaration.
+Higher-order component used to inject state-derived props using registered
+selectors.
+
+_Usage_
+
+```js
+function PriceDisplay( { price, currency } ) {
+ return new Intl.NumberFormat( 'en-US', {
+ style: 'currency',
+ currency,
+ } ).format( price );
+}
+
+const { withSelect } = wp.data;
+
+const HammerPriceDisplay = withSelect( ( select, ownProps ) => {
+ const { getPrice } = select( 'my-shop' );
+ const { currency } = ownProps;
+
+ return {
+ price: getPrice( 'hammer', currency ),
+ };
+} )( PriceDisplay );
+
+// Rendered in the application:
+//
+//
+```
+
+In the above example, when `HammerPriceDisplay` is rendered into an
+application, it will pass the price into the underlying `PriceDisplay`
+component and update automatically if the price of a hammer ever changes in
+the store.
+
+_Parameters_
+
+- _mapSelectToProps_ `Function`: Function called on every state change, expected to return object of props to merge with the component's own props.
+
+_Returns_
+
+- `Component`: Enhanced component with merged state data props.
diff --git a/packages/data/src/components/registry-provider/context.js b/packages/data/src/components/registry-provider/context.js
index 825b89ab9fcc5e..a1fe1a9e56183c 100644
--- a/packages/data/src/components/registry-provider/context.js
+++ b/packages/data/src/components/registry-provider/context.js
@@ -12,6 +12,43 @@ export const Context = createContext( defaultRegistry );
const { Consumer, Provider } = Context;
+/**
+ * A custom react Context consumer exposing the provided `registry` to
+ * children components. Used along with the RegistryProvider.
+ *
+ * You can read more about the react context api here:
+ * https://reactjs.org/docs/context.html#contextprovider
+ *
+ * @example
+ * ```js
+ * const {
+ * RegistryProvider,
+ * RegistryConsumer,
+ * createRegistry
+ * } = wp.data;
+ *
+ * const registry = createRegistry( {} );
+ *
+ * const App = ( { props } ) => {
+ * return
+ * Hello There
+ *
+ * { ( registry ) => (
+ *
+ *
+ * }
+ */
export const RegistryConsumer = Consumer;
+/**
+ * A custom Context provider for exposing the provided `registry` to children
+ * components via a consumer.
+ *
+ * See RegistryConsumer documentation for
+ * example.
+ */
export default Provider;
diff --git a/packages/data/src/components/registry-provider/use-registry.js b/packages/data/src/components/registry-provider/use-registry.js
index 253737a14782eb..846cb5627fd2f3 100644
--- a/packages/data/src/components/registry-provider/use-registry.js
+++ b/packages/data/src/components/registry-provider/use-registry.js
@@ -8,6 +8,45 @@ import { useContext } from '@wordpress/element';
*/
import { Context } from './context';
+/**
+ * A custom react hook exposing the registry context for use.
+ *
+ * This exposes the `registry` value provided via the
+ * Registry Provider to a component implementing
+ * this hook.
+ *
+ * It acts similarly to the `useContext` react hook.
+ *
+ * Note: Generally speaking, `useRegistry` is a low level hook that in most cases
+ * won't be needed for implementation. Most interactions with the wp.data api
+ * can be performed via the `useSelect` hook, or the `withSelect` and
+ * `withDispatch` higher order components.
+ *
+ * @example
+ * ```js
+ * const {
+ * RegistryProvider,
+ * createRegistry,
+ * useRegistry,
+ * } = wp.data
+ *
+ * const registry = createRegistry( {} );
+ *
+ * const SomeChildUsingRegistry = ( props ) => {
+ * const registry = useRegistry( registry );
+ * // ...logic implementing the registry in other react hooks.
+ * };
+ *
+ *
+ * const ParentProvidingRegistry = ( props ) => {
+ * return
+ *
+ *
+ * };
+ * ```
+ *
+ * @return {Function} A custom react hook exposing the registry context value.
+ */
export default function useRegistry() {
return useContext( Context );
}
diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js
index 64b975f7baeaad..d71f72b21c917f 100644
--- a/packages/data/src/components/use-select/index.js
+++ b/packages/data/src/components/use-select/index.js
@@ -31,6 +31,49 @@ const useIsomorphicLayoutEffect =
const renderQueue = createQueue();
+/**
+ * Custom react hook for retrieving props from registered selectors.
+ *
+ * In general, this custom React hook follows the
+ * [rules of hooks](https://reactjs.org/docs/hooks-rules.html).
+ *
+ * @param {Function} _mapSelect Function called on every state change. The
+ * returned value is exposed to the component
+ * implementing this hook. The function receives
+ * the `registry.select` method on the first
+ * argument and the `registry` on the second
+ * argument.
+ * @param {Array} deps If provided, this memoizes the mapSelect so the
+ * same `mapSelect` is invoked on every state
+ * change unless the dependencies change.
+ *
+ * @example
+ * ```js
+ * const { useSelect } = wp.data;
+ *
+ * function HammerPriceDisplay( { currency } ) {
+ * const price = useSelect( ( select ) => {
+ * return select( 'my-shop' ).getPrice( 'hammer', currency )
+ * }, [ currency ] );
+ * return new Intl.NumberFormat( 'en-US', {
+ * style: 'currency',
+ * currency,
+ * } ).format( price );
+ * }
+ *
+ * // Rendered in the application:
+ * //
+ * ```
+ *
+ * In the above example, when `HammerPriceDisplay` is rendered into an
+ * application, the price will be retrieved from the store state using the
+ * `mapSelect` callback on `useSelect`. If the currency prop changes then
+ * any price in the state for that currency is retrieved. If the currency prop
+ * doesn't change and other props are passed in that do change, the price will
+ * not change because the dependency is just the currency.
+ *
+ * @return {Function} A custom react hook.
+ */
export default function useSelect( _mapSelect, deps ) {
const mapSelect = useCallback( _mapSelect, deps );
const registry = useRegistry();
diff --git a/packages/data/src/components/with-select/index.js b/packages/data/src/components/with-select/index.js
index e1d04c6b48dc2d..f9659e7e865a8f 100644
--- a/packages/data/src/components/with-select/index.js
+++ b/packages/data/src/components/with-select/index.js
@@ -1,2 +1,66 @@
-export { default as withSelect } from './with-select-new';
+/**
+ * WordPress dependencies
+ */
+import { createHigherOrderComponent, pure } from '@wordpress/compose';
+/**
+ * Internal dependencies
+ */
+import useSelect from '../use-select';
+
+/**
+ * Higher-order component used to inject state-derived props using registered
+ * selectors.
+ *
+ * @param {Function} mapSelectToProps Function called on every state change,
+ * expected to return object of props to
+ * merge with the component's own props.
+ *
+ * @example
+ * ```js
+ * function PriceDisplay( { price, currency } ) {
+ * return new Intl.NumberFormat( 'en-US', {
+ * style: 'currency',
+ * currency,
+ * } ).format( price );
+ * }
+ *
+ * const { withSelect } = wp.data;
+ *
+ * const HammerPriceDisplay = withSelect( ( select, ownProps ) => {
+ * const { getPrice } = select( 'my-shop' );
+ * const { currency } = ownProps;
+ *
+ * return {
+ * price: getPrice( 'hammer', currency ),
+ * };
+ * } )( PriceDisplay );
+ *
+ * // Rendered in the application:
+ * //
+ * //
+ * ```
+ * In the above example, when `HammerPriceDisplay` is rendered into an
+ * application, it will pass the price into the underlying `PriceDisplay`
+ * component and update automatically if the price of a hammer ever changes in
+ * the store.
+ *
+ * @return {Component} Enhanced component with merged state data props.
+ */
+const withSelect = ( mapSelectToProps ) => createHigherOrderComponent(
+ ( WrappedComponent ) => pure(
+ ( ownProps ) => {
+ const mapSelect =
+ ( select, registry ) => mapSelectToProps(
+ select,
+ ownProps,
+ registry
+ );
+ const mergeProps = useSelect( mapSelect );
+ return ;
+ }
+ ),
+ 'withSelect'
+);
+
+export default withSelect;
diff --git a/packages/data/src/components/with-select/test/index.js b/packages/data/src/components/with-select/test/index.js
index 8149e661e6a484..3b2049826c63b5 100644
--- a/packages/data/src/components/with-select/test/index.js
+++ b/packages/data/src/components/with-select/test/index.js
@@ -12,7 +12,7 @@ import { Component } from '@wordpress/element';
/**
* Internal dependencies
*/
-import { withSelect } from '../';
+import withSelect from '../';
import withDispatch from '../../with-dispatch';
import { createRegistry } from '../../../registry';
import { RegistryProvider } from '../../registry-provider';
diff --git a/packages/data/src/components/with-select/with-select-new.js b/packages/data/src/components/with-select/with-select-new.js
deleted file mode 100644
index 2476d7a784d887..00000000000000
--- a/packages/data/src/components/with-select/with-select-new.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { createHigherOrderComponent, pure } from '@wordpress/compose';
-
-/**
- * Internal dependencies
- */
-import useSelect from '../use-select';
-
-const withSelect = ( mapSelectToProps ) => createHigherOrderComponent(
- ( WrappedComponent ) => pure(
- ( ownProps ) => {
- const mapSelect =
- ( select, registry ) => mapSelectToProps(
- select,
- ownProps,
- registry
- );
- const mergeProps = useSelect( mapSelect );
- return ;
- }
- ),
- 'withSelect'
-);
-
-export default withSelect;
diff --git a/packages/data/src/components/with-select/with-select-old.js b/packages/data/src/components/with-select/with-select-old.js
deleted file mode 100644
index d2fd4ae462b3bb..00000000000000
--- a/packages/data/src/components/with-select/with-select-old.js
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { Component } from '@wordpress/element';
-import { isShallowEqualObjects } from '@wordpress/is-shallow-equal';
-import { createHigherOrderComponent } from '@wordpress/compose';
-import { createQueue } from '@wordpress/priority-queue';
-
-/**
- * Internal dependencies
- */
-import { RegistryConsumer } from '../registry-provider';
-import { AsyncModeConsumer } from '../async-mode-provider';
-
-const renderQueue = createQueue();
-
-/**
- * Higher-order component used to inject state-derived props using registered
- * selectors.
- *
- * @param {Function} mapSelectToProps Function called on every state change,
- * expected to return object of props to
- * merge with the component's own props.
- *
- * @example
- * ```js
- * function PriceDisplay( { price, currency } ) {
- * return new Intl.NumberFormat( 'en-US', {
- * style: 'currency',
- * currency,
- * } ).format( price );
- * }
- *
- * const { withSelect } = wp.data;
- *
- * const HammerPriceDisplay = withSelect( ( select, ownProps ) => {
- * const { getPrice } = select( 'my-shop' );
- * const { currency } = ownProps;
- *
- * return {
- * price: getPrice( 'hammer', currency ),
- * };
- * } )( PriceDisplay );
- *
- * // Rendered in the application:
- * //
- * //
- * ```
- * In the above example, when `HammerPriceDisplay` is rendered into an application, it will pass the price into the underlying `PriceDisplay` component and update automatically if the price of a hammer ever changes in the store.
- *
- * @return {Component} Enhanced component with merged state data props.
- */
-const withSelect = ( mapSelectToProps ) => createHigherOrderComponent( ( WrappedComponent ) => {
- /**
- * Default merge props. A constant value is used as the fallback since it
- * can be more efficiently shallow compared in case component is repeatedly
- * rendered without its own merge props.
- *
- * @type {Object}
- */
- const DEFAULT_MERGE_PROPS = {};
-
- /**
- * Given a props object, returns the next merge props by mapSelectToProps.
- *
- * @param {Object} props Props to pass as argument to mapSelectToProps.
- *
- * @return {Object} Props to merge into rendered wrapped element.
- */
- function getNextMergeProps( props ) {
- return (
- mapSelectToProps( props.registry.select, props.ownProps, props.registry ) ||
- DEFAULT_MERGE_PROPS
- );
- }
-
- class ComponentWithSelect extends Component {
- constructor( props ) {
- super( props );
-
- this.onStoreChange = this.onStoreChange.bind( this );
-
- this.subscribe( props.registry );
-
- this.mergeProps = getNextMergeProps( props );
- }
-
- componentDidMount() {
- this.canRunSelection = true;
-
- // A state change may have occurred between the constructor and
- // mount of the component (e.g. during the wrapped component's own
- // constructor), in which case selection should be rerun.
- if ( this.hasQueuedSelection ) {
- this.hasQueuedSelection = false;
- this.onStoreChange();
- }
- }
-
- componentWillUnmount() {
- this.canRunSelection = false;
- this.unsubscribe();
- renderQueue.flush( this );
- }
-
- shouldComponentUpdate( nextProps, nextState ) {
- // Cycle subscription if registry changes.
- const hasRegistryChanged = nextProps.registry !== this.props.registry;
- const hasSyncRenderingChanged = nextProps.isAsync !== this.props.isAsync;
-
- if ( hasRegistryChanged ) {
- this.unsubscribe();
- this.subscribe( nextProps.registry );
- }
-
- if ( hasSyncRenderingChanged ) {
- renderQueue.flush( this );
- }
-
- // Treat a registry change as equivalent to `ownProps`, to reflect
- // `mergeProps` to rendered component if and only if updated.
- const hasPropsChanged = (
- hasRegistryChanged ||
- ! isShallowEqualObjects( this.props.ownProps, nextProps.ownProps )
- );
-
- // Only render if props have changed or merge props have been updated
- // from the store subscriber.
- if ( this.state === nextState && ! hasPropsChanged && ! hasSyncRenderingChanged ) {
- return false;
- }
-
- if ( hasPropsChanged || hasSyncRenderingChanged ) {
- const nextMergeProps = getNextMergeProps( nextProps );
- if ( ! isShallowEqualObjects( this.mergeProps, nextMergeProps ) ) {
- // If merge props change as a result of the incoming props,
- // they should be reflected as such in the upcoming render.
- // While side effects are discouraged in lifecycle methods,
- // this component is used heavily, and prior efforts to use
- // `getDerivedStateFromProps` had demonstrated miserable
- // performance.
- this.mergeProps = nextMergeProps;
- }
-
- // Regardless whether merge props are changing, fall through to
- // incur the render since the component will need to receive
- // the changed `ownProps`.
- }
-
- return true;
- }
-
- onStoreChange() {
- if ( ! this.canRunSelection ) {
- this.hasQueuedSelection = true;
- return;
- }
-
- const nextMergeProps = getNextMergeProps( this.props );
- if ( isShallowEqualObjects( this.mergeProps, nextMergeProps ) ) {
- return;
- }
-
- this.mergeProps = nextMergeProps;
-
- // Schedule an update. Merge props are not assigned to state since
- // derivation of merge props from incoming props occurs within
- // shouldComponentUpdate, where setState is not allowed. setState
- // is used here instead of forceUpdate because forceUpdate bypasses
- // shouldComponentUpdate altogether, which isn't desireable if both
- // state and props change within the same render. Unfortunately,
- // this requires that next merge props are generated twice.
- this.setState( {} );
- }
-
- subscribe( registry ) {
- this.unsubscribe = registry.subscribe( () => {
- if ( this.props.isAsync ) {
- renderQueue.add( this, this.onStoreChange );
- } else {
- this.onStoreChange();
- }
- } );
- }
-
- render() {
- return ;
- }
- }
-
- return ( ownProps ) => (
-
- { ( isAsync ) => (
-
- { ( registry ) => (
-
- ) }
-
- ) }
-
- );
-}, 'withSelect' );
-
-export default withSelect;
diff --git a/packages/data/src/index.js b/packages/data/src/index.js
index d9c7c32ea75fc1..46c830289b91d7 100644
--- a/packages/data/src/index.js
+++ b/packages/data/src/index.js
@@ -9,13 +9,17 @@ import combineReducers from 'turbo-combine-reducers';
import defaultRegistry from './default-registry';
import * as plugins from './plugins';
-export { withSelect } from './components/with-select';
+export { default as withSelect } from './components/with-select';
export { default as withDispatch } from './components/with-dispatch';
export { default as withRegistry } from './components/with-registry';
-export { RegistryProvider, RegistryConsumer } from './components/registry-provider';
+export {
+ RegistryProvider,
+ RegistryConsumer,
+ useRegistry,
+} from './components/registry-provider';
+export { default as useSelect } from './components/use-select';
export {
AsyncModeProvider as __experimentalAsyncModeProvider,
- useAsyncMode as __experimentalUseAsyncMode,
} from './components/async-mode-provider';
export { createRegistry } from './registry';
export { plugins };