From 71f4a8d781e65dcc1b58a36143a52b0d2ab243d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Fri, 1 Mar 2019 19:33:49 +0100 Subject: [PATCH 1/9] Autogenerated docs and setup for data --- packages/data/README.md | 190 +++++++++++++++++++++++--- packages/data/package.json | 6 + packages/data/undocumented-exports.md | 9 ++ 3 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 packages/data/undocumented-exports.md diff --git a/packages/data/README.md b/packages/data/README.md index 47082c451ec70..c123d658ebb42 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -102,12 +102,12 @@ registerStore( 'my-shop', { The return value of `registerStore` is a [Redux-like store object](https://redux.js.org/docs/basics/Store.html) with the following methods: -- `store.getState()`: Returns the state value of the registered reducer - - _Redux parallel:_ [`getState`](https://redux.js.org/api-reference/store#getState) -- `store.subscribe( listener: Function )`: Registers a function called any time the value of state changes. - - _Redux parallel:_ [`subscribe`](https://redux.js.org/api-reference/store#subscribe(listener)) -- `store.dispatch( action: Object )`: Given an action object, calls the registered reducer and updates the state value. - - _Redux parallel:_ [`dispatch`](https://redux.js.org/api-reference/store#dispatch(action)) +- `store.getState()`: Returns the state value of the registered reducer + - _Redux parallel:_ [`getState`](https://redux.js.org/api-reference/store#getState) +- `store.subscribe( listener: Function )`: Registers a function called any time the value of state changes. + - _Redux parallel:_ [`subscribe`](https://redux.js.org/api-reference/store#subscribe(listener)) +- `store.dispatch( action: Object )`: Given an action object, calls the registered reducer and updates the state value. + - _Redux parallel:_ [`dispatch`](https://redux.js.org/api-reference/store#dispatch(action)) ## Options @@ -317,19 +317,19 @@ const SaleButton = withDispatch( ( dispatch, ownProps, { select } ) => { // Start Sale! ``` -*Note:* It is important that the `mapDispatchToProps` function always returns an object with the same keys. For example, it should not contain conditions under which a different value would be returned. +_Note:_ It is important that the `mapDispatchToProps` function always returns an object with the same keys. For example, it should not contain conditions under which a different value would be returned. ## 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). +- `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. @@ -405,7 +405,6 @@ function createCustomStore() { registry.registerGenericStore( 'custom-data', createCustomStore() ); ``` - ## 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. @@ -414,11 +413,160 @@ The [higher-order components](#higher-order-components) were created to compleme Specific implementation differences from Redux and React Redux: -- In Redux, a `subscribe` listener is called on every dispatch, regardless of whether the value of state has changed. - - In `@wordpress/data`, a subscriber is only called when state has changed. -- In React Redux, a `mapStateToProps` function must return an object. - - In `@wordpress/data`, a `withSelect` mapping function can return `undefined` if it has no props to inject. -- In React Redux, the `mapDispatchToProps` argument can be defined as an object or a function. - - In `@wordpress/data`, the `withDispatch` higher-order component creator must be passed a function. +- In Redux, a `subscribe` listener is called on every dispatch, regardless of whether the value of state has changed. + - In `@wordpress/data`, a subscriber is only called when state has changed. +- In React Redux, a `mapStateToProps` function must return an object. + - In `@wordpress/data`, a `withSelect` mapping function can return `undefined` if it has no props to inject. +- In React Redux, the `mapDispatchToProps` argument can be defined as an object or a function. + - In `@wordpress/data`, the `withDispatch` higher-order component creator must be passed a function. + +## API + + + +### combineReducers + +[src/index.js#L30-L30](src/index.js#L30-L30) + +The combineReducers helper function turns an object whose values are different +reducing functions into a single reducing function you can pass to registerReducer. + +**Parameters** + +- **reducers** `Object`: An object whose values correspond to different reducing functions that need to be combined into one. + +**Returns** + +`Function` A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape. + +### createRegistry + +[src/index.js#L16-L16](src/index.js#L16-L16) + +Creates a new store registry, given an optional object of initial store +configurations. + +**Parameters** + +- **storeConfigs** `Object`: Initial store configurations. + +**Returns** + +`WPDataRegistry` Data registry. + +### createRegistryControl + +[src/index.js#L18-L18](src/index.js#L18-L18) + +Mark a control as a registry control. + +**Parameters** + +- **registryControl** `function`: Function receiving a registry object and returning a control. + +**Returns** + +`function` marked registry control. + +### createRegistrySelector + +[src/index.js#L18-L18](src/index.js#L18-L18) + +Mark a selector as a registry selector. + +**Parameters** + +- **registrySelector** `function`: Function receiving a registry object and returning a state selector. + +**Returns** + +`function` marked registry selector. + +### dispatch + +[src/index.js#L33-L33](src/index.js#L33-L33) + +Undocumented declaration. + +### plugins + +[src/index.js#L17-L17](src/index.js#L17-L17) + +Undocumented declaration. + +### registerGenericStore + +[src/index.js#L35-L35](src/index.js#L35-L35) + +Undocumented declaration. + +### registerStore + +[src/index.js#L36-L36](src/index.js#L36-L36) + +Undocumented declaration. + +### RegistryConsumer + +[src/index.js#L14-L14](src/index.js#L14-L14) + +Undocumented declaration. + +### RegistryProvider + +[src/index.js#L14-L14](src/index.js#L14-L14) + +Undocumented declaration. + +### select + +[src/index.js#L32-L32](src/index.js#L32-L32) + +Undocumented declaration. + +### subscribe + +[src/index.js#L34-L34](src/index.js#L34-L34) + +Undocumented declaration. + +### use + +[src/index.js#L37-L37](src/index.js#L37-L37) + +Undocumented declaration. + +### withDispatch + +[src/index.js#L13-L13](src/index.js#L13-L13) + +Higher-order component used to add dispatch props using registered action +creators. + +**Parameters** + +- **mapDispatchToProps** `Object`: Object of prop names where value is a dispatch-bound action creator, or a function to be called with with the component's props and returning an action creator. + +**Returns** + +`Component` Enhanced component with merged dispatcher props. + +### withSelect + +[src/index.js#L12-L12](src/index.js#L12-L12) + +Higher-order component used to inject state-derived props using registered +selectors. + +**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. + + +

Code is Poetry.

diff --git a/packages/data/package.json b/packages/data/package.json index 98c243c145b44..aa92c55ef9d02 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -34,7 +34,13 @@ "redux": "^4.0.0", "turbo-combine-reducers": "^1.0.2" }, + "devDependencies": { + "@wordpress/docgen": "file:../docgen" + }, "publishConfig": { "access": "public" + }, + "scripts": { + "docs:generate": "docgen ./src/index.js --output ./README.md --to-token --ignore __experimental*" } } diff --git a/packages/data/undocumented-exports.md b/packages/data/undocumented-exports.md new file mode 100644 index 0000000000000..90cde9b1fdbe9 --- /dev/null +++ b/packages/data/undocumented-exports.md @@ -0,0 +1,9 @@ +# Undocumented exports + +createRegistry +createRegistryControl +createRegistrySelector +plugins +RegistryConsumer +RegistryProvider +use From ed8a2402f7b007b0a8131aa7b3797251f4ecd87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 6 Mar 2019 14:36:45 +0100 Subject: [PATCH 2/9] Use new script --- bin/update-readmes.js | 2 +- packages/data/README.md | 18 ++++++++++++------ packages/data/package.json | 6 ------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bin/update-readmes.js b/bin/update-readmes.js index b649ef5f99603..fa949c30d4bbf 100755 --- a/bin/update-readmes.js +++ b/bin/update-readmes.js @@ -13,7 +13,7 @@ const packages = [ 'block-serialization-default-parser', 'blocks', 'compose', - //'data', + 'data', 'date', 'deprecated', 'dom', diff --git a/packages/data/README.md b/packages/data/README.md index c123d658ebb42..a77a9a6a68f17 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -437,7 +437,7 @@ reducing functions into a single reducing function you can pass to registerReduc **Returns** -`Function` A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape. +`Function`: A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape. ### createRegistry @@ -452,7 +452,7 @@ configurations. **Returns** -`WPDataRegistry` Data registry. +`WPDataRegistry`: Data registry. ### createRegistryControl @@ -466,7 +466,7 @@ Mark a control as a registry control. **Returns** -`function` marked registry control. +`function`: marked registry control. ### createRegistrySelector @@ -480,7 +480,7 @@ Mark a selector as a registry selector. **Returns** -`function` marked registry selector. +`function`: marked registry selector. ### dispatch @@ -549,7 +549,7 @@ creators. **Returns** -`Component` Enhanced component with merged dispatcher props. +`Component`: Enhanced component with merged dispatcher props. ### withSelect @@ -564,7 +564,13 @@ selectors. **Returns** -`Component` Enhanced component with merged state data props. +`Component`: Enhanced component with merged state data props. + +### \_\_experimentalAsyncModeProvider + +[src/index.js#L15-L15](src/index.js#L15-L15) + +Undocumented declaration. diff --git a/packages/data/package.json b/packages/data/package.json index aa92c55ef9d02..98c243c145b44 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -34,13 +34,7 @@ "redux": "^4.0.0", "turbo-combine-reducers": "^1.0.2" }, - "devDependencies": { - "@wordpress/docgen": "file:../docgen" - }, "publishConfig": { "access": "public" - }, - "scripts": { - "docs:generate": "docgen ./src/index.js --output ./README.md --to-token --ignore __experimental*" } } From b25ae00cbf8c75bcf608741938878fd05f37f871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 6 Mar 2019 15:04:44 +0100 Subject: [PATCH 3/9] Update docs --- packages/data/README.md | 375 +++++++++--------- .../src/components/with-dispatch/index.js | 56 ++- .../data/src/components/with-select/index.js | 26 ++ packages/data/src/index.js | 74 ++++ 4 files changed, 334 insertions(+), 197 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index a77a9a6a68f17..e53a6a8029033 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -109,27 +109,27 @@ The return value of `registerStore` is a [Redux-like store object](https://redux - `store.dispatch( action: Object )`: Given an action object, calls the registered reducer and updates the state value. - _Redux parallel:_ [`dispatch`](https://redux.js.org/api-reference/store#dispatch(action)) -## Options +### Options -### `reducer` +#### `reducer` A [**reducer**](https://redux.js.org/docs/basics/Reducers.html) is a function accepting the previous `state` and `action` as arguments and returns an updated `state` value. -### `actions` +#### `actions` The **`actions`** object should describe all [action creators](https://redux.js.org/glossary#action-creator) available for your store. An action creator is a function that optionally accepts arguments and returns an action object to dispatch to the registered reducer. _Dispatching actions is the primary mechanism for making changes to your state._ -### `selectors` +#### `selectors` The **`selectors`** object includes a set of functions for accessing and deriving state values. A selector is a function which accepts state and optional arguments and returns some value from state. _Calling selectors is the primary mechanism for retrieving data from your state_, and serve as a useful abstraction over the raw data which is typically more susceptible to change and less readily usable as a [normalized object](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape#designing-a-normalized-state). -### `resolvers` +#### `resolvers` A **resolver** is a side-effect for a selector. If your selector result may need to be fulfilled from an external source, you can define a resolver such that the first time the selector is called, the fulfillment behavior is effected. The `resolvers` option should be passed as an object where each key is the name of the selector to act upon, the value a function which receives the same arguments passed to the selector, excluding the state argument. It can then dispatch as necessary to fulfill the requirements of the selector, taking advantage of the fact that most data consumers will subscribe to subsequent state changes (by `subscribe` or `withSelect`). -### `controls` +#### `controls` _**Note:** Controls are an opt-in feature, enabled via `use` (the [Plugins API](/packages/data/src/plugins/README.md))._ @@ -139,186 +139,10 @@ The `controls` option should be passed as an object where each key is the name o Refer to the [documentation of `@wordpress/redux-routine`](/packages/redux-routine/README.md) for more information. -### `initialState` +#### `initialState` An optional preloaded initial state for the store. You may use this to restore some serialized state value or a state generated server-side. -## Data Access and Manipulation - -It is very rare that you should access store methods directly. Instead, the following suite of functions and higher-order components is provided for the most common data access and manipulation needs. - -### Data API - -The top-level API of `@wordpress/data` includes a number of functions which allow immediate access to select from and dispatch to a registered store. These are most useful in low-level code where a selector or action dispatch is called a single time or at known intervals. For displaying data in a user interface, you should use [higher-order components](#higher-order-components) instead. - -#### `select( storeName: string ): Object` - -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. - -_Example:_ - -```js -const { select } = wp.data; - -select( 'my-shop' ).getPrice( 'hammer' ); -``` - -#### `dispatch( storeName: string ): Object` - -Given the name of a registered store, returns an object of the store's action creators. Calling an action creator will cause it to be dispatched, updating the state value accordingly. - -_Example:_ - -```js -const { dispatch } = wp.data; - -dispatch( 'my-shop' ).setPrice( 'hammer', 9.75 ); -``` - -#### `subscribe(): Function` - -Given a listener function, the function will be called any time the state value of one of the registered stores has changed. This function returns a `unsubscribe` function used to stop the subscription. - -_Example:_ - -```js -const { subscribe } = wp.data; - -const unsubscribe = subscribe( () => { - // You could use this opportunity to test whether the derived result of a - // selector has subsequently changed as the result of a state update. -} ); - -// Later, if necessary... -unsubscribe(); -``` - -### Helpers - -#### `combineReducers( reducers: Object ): Function` - -As your app grows more complex, you'll want to split your reducing function into separate functions, each managing independent parts of the state. The `combineReducers` helper function turns an object whose values are different reducing functions into a single reducing function you can pass to `registerStore`. - -_Example:_ - -```js -const { combineReducers, registerStore } = wp.data; - -const prices = ( state = {}, action ) => { - return action.type === 'SET_PRICE' ? - { - ...state, - [ action.item ]: action.price, - } : - state; -}; - -const discountPercent = ( state = 0, action ) => { - return action.type === 'START_SALE' ? - action.discountPercent : - state; -}; - -registerStore( 'my-shop', { - reducer: combineReducers( { - prices, - discountPercent, - } ), -} ); -``` - -### Higher-Order Components - -A higher-order component is a function which accepts a [component](/packages/element/README.md) and returns a new, enhanced component. A stateful user interface should respond to changes in the underlying state and updates its displayed element accordingly. WordPress uses higher-order components both as a means to separate the purely visual aspects of an interface from its data backing, and to ensure that the data is kept in-sync with the stores. - -#### `withSelect( mapSelectToProps: Function ): Function` - -Use `withSelect` to inject state-derived props into a component. Passed a function which returns an object mapping prop names to the subscribed data source, a higher-order component function is returned. The higher-order component can be used to enhance a presentational component, updating it automatically when state changes. The mapping function is passed the [`select` function](#select), the props passed to the original component and the `registry` object. - -_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. - -#### `withDispatch( mapDispatchToProps: Function ): Function` - -Use `withDispatch` to inject dispatching action props into your component. Passed a function which returns an object mapping prop names to action dispatchers, a higher-order component function is returned. The higher-order component can be used to enhance a component. For example, you can define callback behaviors as props for responding to user interactions. The mapping function is passed the [`dispatch` function](#dispatch), the props passed to the original component and the `registry` object. - -```jsx -function Button( { onClick, children } ) { - return ; -} - -const { withDispatch } = wp.data; - -const SaleButton = withDispatch( ( dispatch, ownProps ) => { - const { startSale } = dispatch( 'my-shop' ); - const { discountPercent } = ownProps; - - return { - onClick() { - startSale( discountPercent ); - }, - }; -} )( Button ); - -// Rendered in the application: -// -// Start Sale! -``` - -In the majority of cases, it will be sufficient to use only two first params passed to `mapDispatchToProps` as illustrated in the previous example. However, there might be some very advanced use cases where using the `registry` object might be used as a tool to optimize the performance of your component. Using `select` function from the registry might be useful when you need to fetch some dynamic data from the store at the time when the event is fired, but at the same time, you never use it to render your component. In such scenario, you can avoid using the `withSelect` higher order component to compute such prop, which might lead to unnecessary re-renders of you component caused by its frequent value change. Keep in mind, that `mapDispatchToProps` must return an object with functions only. - -```jsx -function Button( { onClick, children } ) { - return ; -} - -const { withDispatch } = wp.data; - -const SaleButton = withDispatch( ( dispatch, ownProps, { select } ) => { - // Stock number changes frequently. - const { getStockNumber } = select( 'my-shop' ); - const { startSale } = dispatch( 'my-shop' ); - - return { - onClick() { - const dicountPercent = getStockNumber() > 50 ? 10 : 20; - startSale( discountPercent ); - }, - }; -} )( Button ); - -// Rendered in the application: -// -// Start Sale! -``` - -_Note:_ It is important that the `mapDispatchToProps` function always returns an object with the same keys. For example, it should not contain conditions under which a different value would be returned. - ## 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: @@ -422,15 +246,45 @@ Specific implementation differences from Redux and React Redux: ## API +It is very rare that you should access store methods directly. Instead, the following suite of functions and higher-order components is provided for the most common data access and manipulation needs. + ### combineReducers -[src/index.js#L30-L30](src/index.js#L30-L30) +[src/index.js#L57-L57](src/index.js#L57-L57) The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to registerReducer. +**Usage** + +```js +const { combineReducers, registerStore } = wp.data; + +const prices = ( state = {}, action ) => { + return action.type === 'SET_PRICE' ? + { + ...state, + [ action.item ]: action.price, + } : + state; +}; + +const discountPercent = ( state = 0, action ) => { + return action.type === 'START_SALE' ? + action.discountPercent : + state; +}; + +registerStore( 'my-shop', { + reducer: combineReducers( { + prices, + discountPercent, + } ), +} ); +``` + **Parameters** - **reducers** `Object`: An object whose values correspond to different reducing functions that need to be combined into one. @@ -484,9 +338,25 @@ Mark a selector as a registry selector. ### dispatch -[src/index.js#L33-L33](src/index.js#L33-L33) +[src/index.js#L88-L88](src/index.js#L88-L88) -Undocumented declaration. +Given the name of a registered store, returns an object of the store's action creators. Calling an action creator will cause it to be dispatched, updating the state value accordingly. + +**Usage** + +```js +const { dispatch } = wp.data; + +dispatch( 'my-shop' ).setPrice( 'hammer', 9.75 ); +``` + +**Parameters** + +- **name** `string`: Store name + +**Returns** + +`Object`: Object containing the action creators. ### plugins @@ -496,13 +366,13 @@ Undocumented declaration. ### registerGenericStore -[src/index.js#L35-L35](src/index.js#L35-L35) +[src/index.js#L109-L109](src/index.js#L109-L109) Undocumented declaration. ### registerStore -[src/index.js#L36-L36](src/index.js#L36-L36) +[src/index.js#L110-L110](src/index.js#L110-L110) Undocumented declaration. @@ -520,19 +390,53 @@ Undocumented declaration. ### select -[src/index.js#L32-L32](src/index.js#L32-L32) +[src/index.js#L73-L73](src/index.js#L73-L73) -Undocumented declaration. +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** + +```js +const { select } = wp.data; + +select( 'my-shop' ).getPrice( 'hammer' ); +``` + +**Parameters** + +- **name** `string`: Store name + +**Returns** + +`Object`: Object containing the store's selectors. ### subscribe -[src/index.js#L34-L34](src/index.js#L34-L34) +[src/index.js#L108-L108](src/index.js#L108-L108) -Undocumented declaration. +Given a listener function, the function will be called any time the state value of one of the registered stores has changed. This function returns a `unsubscribe` function used to stop the subscription. + +**Usage** + +```js +const { subscribe } = wp.data; + +const unsubscribe = subscribe( () => { + // You could use this opportunity to test whether the derived result of a + // selector has subsequently changed as the result of a state update. +} ); + +// Later, if necessary... +unsubscribe(); +``` + +**Parameters** + +- **listener** `Function`: Callback function. ### use -[src/index.js#L37-L37](src/index.js#L37-L37) +[src/index.js#L111-L111](src/index.js#L111-L111) Undocumented declaration. @@ -540,8 +444,61 @@ Undocumented declaration. [src/index.js#L13-L13](src/index.js#L13-L13) -Higher-order component used to add dispatch props using registered action -creators. +Higher-order component used to add dispatch props using registered action creators. + +**Usage** + +```jsx +function Button( { onClick, children } ) { + return ; +} + +const { withDispatch } = wp.data; + +const SaleButton = withDispatch( ( dispatch, ownProps ) => { + const { startSale } = dispatch( 'my-shop' ); + const { discountPercent } = ownProps; + + return { + onClick() { + startSale( discountPercent ); + }, + }; +} )( Button ); + +// Rendered in the application: +// +// Start Sale! +``` + +In the majority of cases, it will be sufficient to use only two first params passed to `mapDispatchToProps` as illustrated in the previous example. However, there might be some very advanced use cases where using the `registry` object might be used as a tool to optimize the performance of your component. Using `select` function from the registry might be useful when you need to fetch some dynamic data from the store at the time when the event is fired, but at the same time, you never use it to render your component. In such scenario, you can avoid using the `withSelect` higher order component to compute such prop, which might lead to unnecessary re-renders of you component caused by its frequent value change. Keep in mind, that `mapDispatchToProps` must return an object with functions only. + +```jsx +function Button( { onClick, children } ) { + return ; +} + +const { withDispatch } = wp.data; + +const SaleButton = withDispatch( ( dispatch, ownProps, { select } ) => { + // Stock number changes frequently. + const { getStockNumber } = select( 'my-shop' ); + const { startSale } = dispatch( 'my-shop' ); + + return { + onClick() { + const dicountPercent = getStockNumber() > 50 ? 10 : 20; + startSale( discountPercent ); + }, + }; +} )( Button ); + +// Rendered in the application: +// +// Start Sale! +``` + +_Note:_ It is important that the `mapDispatchToProps` function always returns an object with the same keys. For example, it should not contain conditions under which a different value would be returned. **Parameters** @@ -558,6 +515,34 @@ creators. 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. diff --git a/packages/data/src/components/with-dispatch/index.js b/packages/data/src/components/with-dispatch/index.js index 6c5944085a694..1a13b6f1f7ed1 100644 --- a/packages/data/src/components/with-dispatch/index.js +++ b/packages/data/src/components/with-dispatch/index.js @@ -15,8 +15,7 @@ import { createHigherOrderComponent } from '@wordpress/compose'; import { RegistryConsumer } from '../registry-provider'; /** - * Higher-order component used to add dispatch props using registered action - * creators. + * Higher-order component used to add dispatch props using registered action creators. * * @param {Object} mapDispatchToProps Object of prop names where value is a * dispatch-bound action creator, or a @@ -24,6 +23,59 @@ import { RegistryConsumer } from '../registry-provider'; * component's props and returning an * action creator. * + * @example + * ```jsx + * function Button( { onClick, children } ) { + * return ; + * } + * + * const { withDispatch } = wp.data; + * + * const SaleButton = withDispatch( ( dispatch, ownProps ) => { + * const { startSale } = dispatch( 'my-shop' ); + * const { discountPercent } = ownProps; + * + * return { + * onClick() { + * startSale( discountPercent ); + * }, + * }; + * } )( Button ); + * + * // Rendered in the application: + * // + * // Start Sale! + * ``` + * + * @example + * In the majority of cases, it will be sufficient to use only two first params passed to `mapDispatchToProps` as illustrated in the previous example. However, there might be some very advanced use cases where using the `registry` object might be used as a tool to optimize the performance of your component. Using `select` function from the registry might be useful when you need to fetch some dynamic data from the store at the time when the event is fired, but at the same time, you never use it to render your component. In such scenario, you can avoid using the `withSelect` higher order component to compute such prop, which might lead to unnecessary re-renders of you component caused by its frequent value change. Keep in mind, that `mapDispatchToProps` must return an object with functions only. + * + * ```jsx + * function Button( { onClick, children } ) { + * return ; + * } + * + * const { withDispatch } = wp.data; + * + * const SaleButton = withDispatch( ( dispatch, ownProps, { select } ) => { + * // Stock number changes frequently. + * const { getStockNumber } = select( 'my-shop' ); + * const { startSale } = dispatch( 'my-shop' ); + * + * return { + * onClick() { + * const dicountPercent = getStockNumber() > 50 ? 10 : 20; + * startSale( discountPercent ); + * }, + * }; + * } )( Button ); + * + * // Rendered in the application: + * // + * // Start Sale! + * ``` + * _Note:_ It is important that the `mapDispatchToProps` function always returns an object with the same keys. For example, it should not contain conditions under which a different value would be returned. + * * @return {Component} Enhanced component with merged dispatcher props. */ const withDispatch = ( mapDispatchToProps ) => createHigherOrderComponent( diff --git a/packages/data/src/components/with-select/index.js b/packages/data/src/components/with-select/index.js index 8820238e9f390..d2fd4ae462b3b 100644 --- a/packages/data/src/components/with-select/index.js +++ b/packages/data/src/components/with-select/index.js @@ -22,6 +22,32 @@ const renderQueue = createQueue(); * 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 ) => { diff --git a/packages/data/src/index.js b/packages/data/src/index.js index b87a42b608f2b..ce8f7a32140ac 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -25,13 +25,87 @@ export { createRegistrySelector, createRegistryControl } from './factory'; * @param {Object} reducers An object whose values correspond to different reducing * functions that need to be combined into one. * + * @example + * ```js + * const { combineReducers, registerStore } = wp.data; + * + * const prices = ( state = {}, action ) => { + * return action.type === 'SET_PRICE' ? + * { + * ...state, + * [ action.item ]: action.price, + * } : + * state; + * }; + * + * const discountPercent = ( state = 0, action ) => { + * return action.type === 'START_SALE' ? + * action.discountPercent : + * state; + * }; + * + * registerStore( 'my-shop', { + * reducer: combineReducers( { + * prices, + * discountPercent, + * } ), + * } ); + * ``` + * * @return {Function} A reducer that invokes every reducer inside the reducers * object, and constructs a state object with the same shape. */ export { combineReducers }; +/** + * 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. + * + * @param {string} name Store name + * + * @example + * ```js + * const { select } = wp.data; + * + * select( 'my-shop' ).getPrice( 'hammer' ); + * ``` + * + * @return {Object} Object containing the store's selectors. + */ export const select = defaultRegistry.select; + +/** + * Given the name of a registered store, returns an object of the store's action creators. Calling an action creator will cause it to be dispatched, updating the state value accordingly. + * + * @param {string} name Store name + * + * @example + * ```js + * const { dispatch } = wp.data; + * + * dispatch( 'my-shop' ).setPrice( 'hammer', 9.75 ); + * ``` + * @return {Object} Object containing the action creators. + */ export const dispatch = defaultRegistry.dispatch; + +/** + * Given a listener function, the function will be called any time the state value of one of the registered stores has changed. This function returns a `unsubscribe` function used to stop the subscription. + * + * @param {Function} listener Callback function. + * + * @example + * ```js + * const { subscribe } = wp.data; + * + * const unsubscribe = subscribe( () => { + * // You could use this opportunity to test whether the derived result of a + * // selector has subsequently changed as the result of a state update. + * } ); + * + * // Later, if necessary... + * unsubscribe(); + * ``` + */ export const subscribe = defaultRegistry.subscribe; export const registerGenericStore = defaultRegistry.registerGenericStore; export const registerStore = defaultRegistry.registerStore; From 1d0e56d76c330852e30bc6aa2c5a75d8560cecda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 6 Mar 2019 15:54:04 +0100 Subject: [PATCH 4/9] Update docs --- packages/data/README.md | 24 +++++++++++++++++++----- packages/data/src/index.js | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index e53a6a8029033..1fb28a048e2a8 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -366,15 +366,29 @@ Undocumented declaration. ### registerGenericStore -[src/index.js#L109-L109](src/index.js#L109-L109) +[src/index.js#L116-L116](src/index.js#L116-L116) -Undocumented declaration. +Registers a generic store. + +**Parameters** + +- **key** `string`: Store registry key. +- **config** `Object`: Configuration (getSelectors, getActions, subscribe). ### registerStore -[src/index.js#L110-L110](src/index.js#L110-L110) +[src/index.js#L126-L126](src/index.js#L126-L126) -Undocumented declaration. +Registers a standard `@wordpress/data` store. + +**Parameters** + +- **reducerKey** `string`: Reducer key. +- **options** `Object`: Store description (reducer, actions, selectors, resolvers). + +**Returns** + +`Object`: Registered store object. ### RegistryConsumer @@ -436,7 +450,7 @@ unsubscribe(); ### use -[src/index.js#L111-L111](src/index.js#L111-L111) +[src/index.js#L127-L127](src/index.js#L127-L127) Undocumented declaration. diff --git a/packages/data/src/index.js b/packages/data/src/index.js index ce8f7a32140ac..18b3a0a642dae 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -107,6 +107,22 @@ export const dispatch = defaultRegistry.dispatch; * ``` */ export const subscribe = defaultRegistry.subscribe; + +/** +* Registers a generic store. +* +* @param {string} key Store registry key. +* @param {Object} config Configuration (getSelectors, getActions, subscribe). +*/ export const registerGenericStore = defaultRegistry.registerGenericStore; + +/** + * Registers a standard `@wordpress/data` store. + * + * @param {string} reducerKey Reducer key. + * @param {Object} options Store description (reducer, actions, selectors, resolvers). + * + * @return {Object} Registered store object. + */ export const registerStore = defaultRegistry.registerStore; export const use = defaultRegistry.use; From 777332d47c6fe560586a81739efe8f5cb8bb3ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 6 Mar 2019 15:57:52 +0100 Subject: [PATCH 5/9] Should have ignored experimental/unstable symbols --- packages/data/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index 1fb28a048e2a8..a5e2adb6f7d14 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -565,12 +565,6 @@ In the above example, when `HammerPriceDisplay` is rendered into an application, `Component`: Enhanced component with merged state data props. -### \_\_experimentalAsyncModeProvider - -[src/index.js#L15-L15](src/index.js#L15-L15) - -Undocumented declaration. - From 4c94975ecd57720fa6bc7c8369f9191d0f7351eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Fri, 8 Mar 2019 12:29:46 +0100 Subject: [PATCH 6/9] Delete file added by accident --- packages/data/undocumented-exports.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 packages/data/undocumented-exports.md diff --git a/packages/data/undocumented-exports.md b/packages/data/undocumented-exports.md deleted file mode 100644 index 90cde9b1fdbe9..0000000000000 --- a/packages/data/undocumented-exports.md +++ /dev/null @@ -1,9 +0,0 @@ -# Undocumented exports - -createRegistry -createRegistryControl -createRegistrySelector -plugins -RegistryConsumer -RegistryProvider -use From f882031f27e3ebbf6cdf494e096ef3e5220e9e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 20 Mar 2019 10:11:01 +0100 Subject: [PATCH 7/9] Remove paragraph that no longer makes sense --- packages/data/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index a5e2adb6f7d14..11a8d27edc2f5 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -246,8 +246,6 @@ Specific implementation differences from Redux and React Redux: ## API -It is very rare that you should access store methods directly. Instead, the following suite of functions and higher-order components is provided for the most common data access and manipulation needs. - ### combineReducers From 153a6b41849b9870f2daa514d7ccf12972f0ec83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 20 Mar 2019 10:12:52 +0100 Subject: [PATCH 8/9] Cap description line length --- packages/data/src/index.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 18b3a0a642dae..ae786ee99b4ca 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -58,7 +58,9 @@ export { createRegistrySelector, createRegistryControl } from './factory'; export { combineReducers }; /** - * 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. + * 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. * * @param {string} name Store name * @@ -74,7 +76,8 @@ export { combineReducers }; export const select = defaultRegistry.select; /** - * Given the name of a registered store, returns an object of the store's action creators. Calling an action creator will cause it to be dispatched, updating the state value accordingly. + * Given the name of a registered store, returns an object of the store's action creators. + * Calling an action creator will cause it to be dispatched, updating the state value accordingly. * * @param {string} name Store name * @@ -89,7 +92,9 @@ export const select = defaultRegistry.select; export const dispatch = defaultRegistry.dispatch; /** - * Given a listener function, the function will be called any time the state value of one of the registered stores has changed. This function returns a `unsubscribe` function used to stop the subscription. + * Given a listener function, the function will be called any time the state value + * of one of the registered stores has changed. This function returns a `unsubscribe` + * function used to stop the subscription. * * @param {Function} listener Callback function. * From 0bff3749b403685a07d4ae4ab96572f762ccf88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Wed, 20 Mar 2019 10:13:48 +0100 Subject: [PATCH 9/9] Update README --- packages/data/README.md | 52 ++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index 11a8d27edc2f5..98a3f2bc03d08 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -250,7 +250,7 @@ Specific implementation differences from Redux and React Redux: ### combineReducers -[src/index.js#L57-L57](src/index.js#L57-L57) +[src/index.js#L58-L58](src/index.js#L58-L58) The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to registerReducer. @@ -293,7 +293,7 @@ registerStore( 'my-shop', { ### createRegistry -[src/index.js#L16-L16](src/index.js#L16-L16) +[src/index.js#L17-L17](src/index.js#L17-L17) Creates a new store registry, given an optional object of initial store configurations. @@ -308,7 +308,7 @@ configurations. ### createRegistryControl -[src/index.js#L18-L18](src/index.js#L18-L18) +[src/index.js#L19-L19](src/index.js#L19-L19) Mark a control as a registry control. @@ -322,7 +322,7 @@ Mark a control as a registry control. ### createRegistrySelector -[src/index.js#L18-L18](src/index.js#L18-L18) +[src/index.js#L19-L19](src/index.js#L19-L19) Mark a selector as a registry selector. @@ -336,9 +336,10 @@ Mark a selector as a registry selector. ### dispatch -[src/index.js#L88-L88](src/index.js#L88-L88) +[src/index.js#L92-L92](src/index.js#L92-L92) -Given the name of a registered store, returns an object of the store's action creators. Calling an action creator will cause it to be dispatched, updating the state value accordingly. +Given the name of a registered store, returns an object of the store's action creators. +Calling an action creator will cause it to be dispatched, updating the state value accordingly. **Usage** @@ -358,13 +359,13 @@ dispatch( 'my-shop' ).setPrice( 'hammer', 9.75 ); ### plugins -[src/index.js#L17-L17](src/index.js#L17-L17) +[src/index.js#L18-L18](src/index.js#L18-L18) Undocumented declaration. ### registerGenericStore -[src/index.js#L116-L116](src/index.js#L116-L116) +[src/index.js#L122-L122](src/index.js#L122-L122) Registers a generic store. @@ -375,7 +376,7 @@ Registers a generic store. ### registerStore -[src/index.js#L126-L126](src/index.js#L126-L126) +[src/index.js#L132-L132](src/index.js#L132-L132) Registers a standard `@wordpress/data` store. @@ -390,21 +391,23 @@ Registers a standard `@wordpress/data` store. ### RegistryConsumer -[src/index.js#L14-L14](src/index.js#L14-L14) +[src/index.js#L15-L15](src/index.js#L15-L15) Undocumented declaration. ### RegistryProvider -[src/index.js#L14-L14](src/index.js#L14-L14) +[src/index.js#L15-L15](src/index.js#L15-L15) Undocumented declaration. ### select -[src/index.js#L73-L73](src/index.js#L73-L73) +[src/index.js#L76-L76](src/index.js#L76-L76) -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. +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** @@ -424,9 +427,11 @@ select( 'my-shop' ).getPrice( 'hammer' ); ### subscribe -[src/index.js#L108-L108](src/index.js#L108-L108) +[src/index.js#L114-L114](src/index.js#L114-L114) -Given a listener function, the function will be called any time the state value of one of the registered stores has changed. This function returns a `unsubscribe` function used to stop the subscription. +Given a listener function, the function will be called any time the state value +of one of the registered stores has changed. This function returns a `unsubscribe` +function used to stop the subscription. **Usage** @@ -448,7 +453,7 @@ unsubscribe(); ### use -[src/index.js#L127-L127](src/index.js#L127-L127) +[src/index.js#L133-L133](src/index.js#L133-L133) Undocumented declaration. @@ -520,6 +525,21 @@ _Note:_ It is important that the `mapDispatchToProps` function always returns an `Component`: Enhanced component with merged dispatcher props. +### withRegistry + +[src/index.js#L14-L14](src/index.js#L14-L14) + +Higher-order component which renders the original component with the current +registry context passed as its `registry` prop. + +**Parameters** + +- **OriginalComponent** `WPComponent`: Original component. + +**Returns** + +`WPComponent`: Enhanced component. + ### withSelect [src/index.js#L12-L12](src/index.js#L12-L12)