Docs updated injector docs to reflect inject() changes (#7768)

* Docs updated injector docs to reflect inject() changes and added example for the change

* Minor docs revision

* Added notes about context
This commit is contained in:
Chris Joe 2018-01-27 09:18:45 +13:00 committed by Aaron Carlino
parent 76d2db12b0
commit f1628e3252

View File

@ -57,20 +57,20 @@ The following example is taken from the [Redux Github page](https://github.com/r
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
return state + 1;
case 'DECREMENT':
return state - 1
return state - 1;
default:
return state
}
}
let store = createStore(counter)
let store = createStore(counter);
store.subscribe(() =>
console.log(store.getState())
)
);
// Call an action
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'INCREMENT' });
// 1
```
@ -169,12 +169,13 @@ export default LoggingService;
Now, let's add some middleware to that service. The signature of middleware is:
```js
(next) => (args) => next(args)
const middleware = (next) => (args) => next(args);
```
Where `next()` is the next customisation in the "chain" of middleware. Before invoking the next implementation, you can add whatever customisations you need. Here's how we would use middleware to enhance `LoggingService`.
```js
import thirdPartyLogger from 'third-party-logger';
const addLoggingMiddleware = (next) => (error) => {
if (error.type === LoggingService.CRITICAL) {
thirdpartyLogger.send(error.message);
@ -379,16 +380,16 @@ Using the `PhotoItem` example above, let's create a customised `PhotoItem` that
```js
const enhancedPhoto = (PhotoItem) => (props) => {
const badge = props.isNew ?
<div className="badge">New!</div> :
null;
const badge = props.isNew ?
<div className="badge">New!</div> :
null;
return (
<div>
{badge}
<PhotoItem {...props} />
</div>
);
return (
<div>
{badge}
<PhotoItem {...props} />
</div>
);
}
const EnhancedPhotoItem = enhancedPhoto(PhotoItem);
@ -400,23 +401,22 @@ Alternatively, this component could be expressed with an ES6 class, rather than
function.
```js
const enhancedPhoto = (PhotoItem) => {
return class EnhancedPhotoItem extends React.Component {
render() {
const badge = this.props.isNew ?
<div className="badge">New!</div> :
null;
const enhancedPhoto = (PhotoItem) => (
class EnhancedPhotoItem extends React.Component {
render() {
const badge = this.props.isNew ?
<div className="badge">New!</div> :
null;
return (
<div>
{badge}
<PhotoItem {...this.props} />
</div>
);
}
return (
<div>
{badge}
<PhotoItem {...this.props} />
</div>
);
}
}
}
);
```
When components are stateless, using a simple function in lieu of a class is recommended.
@ -428,12 +428,15 @@ If your component has dependencies, you can add them via the injector using the
higher order component. The function accepts the following arguments:
```js
inject([dependencies], mapDependenciesToProps)(Component)
inject([dependencies], mapDependenciesToProps, getContext)(Component)
```
* **[dependencies]**: An array of dependencies (or a string, if just one)
* **mapDependenciesToProps**: (optional) All dependencies are passed into this function as params. The function
is expected to return a map of props to dependencies. If this parameter is not specified,
the prop names and the service names will mirror each other.
* **getContext**: A callback function with params `(props, currentContext)` that will calculate the context to
use for determining which tranformations apply to the dependencies. This defaults to the current context. This
could help when any customisations that may calls for a change (or tweak) to the current context.
The result is a function that is ready to apply to a component.
@ -450,9 +453,7 @@ __my-module/js/components/Gallery.js__
import React from 'react';
import { inject } from 'lib/Injector';
class Gallery extends React.Component
{
class Gallery extends React.Component {
render() {
const { SearchComponent, ItemComponent } = this.props;
return (
@ -466,14 +467,59 @@ class Gallery extends React.Component
}
}
export default inject(
Gallery,
export default inject(
['GalleryItem', 'SearchBar'],
(GalleryItem, SearchBar) => ({
ItemComponent: GalleryItem,
SearchComponent: SearchBar
})
);
}),
() => 'Gallery.Search'
)(Gallery);
```
The properties used by `inject()` are soft-supplied. This means a parent calling a component that uses
`inject()` could choose to overwrite the dependencies which `inject()` would have otherwise supplied.
Here is an example using the above `Gallery` component with the dependency `ItemComponent` overwritten by the
calling component. We pull in a previously registered `PreviewItem` to replace the former `GalleryItem`.
__my-module/js/components/PreviewSection.js__
```js
import React from 'react';
import { inject } from 'lib/Injector';
class PreviewSection extends React.Component {
render() {
const { Gallery, PreviewItem } = this.props;
return (
<div className="preview-section">
<div className="preview-sidebar">Sidebar here</div>
<Gallery ItemComponent={PreviewItem}/>
</div>
);
}
}
export default inject(
['Gallery', 'PreviewItem']
)(PreviewSection);
```
Another way to provide context to injector is by using the `provideContext` HOC, rather than
the `getContext` param in `inject()`.
__my-module/js/components/ContextualSection.js__
```js
import React, { Component } from 'react';
import { provideContext, inject } from 'lib/Injector';
class MySection extends Component {
// ... section code here ...
}
export default compose(
provideContext('Gallery.Search'),
inject(['Gallery'])
)(MySection);
```
## Using the injector directly within your component
@ -489,7 +535,7 @@ class MyGallery extends React.Component
render () {
<div>
{this.props.items.map(item => {
const Component = this.context.injector.get(item.type);
const Component = this.context.injector.get(item.type, 'Reports.context');
return <Component title={item.title} image={item.image} />
})}
</div>
@ -499,13 +545,23 @@ class MyGallery extends React.Component
export default withInjector(MyGallery);
```
The `Reports.context` in the second parameter provides a context for the injector to determine
which transformations to apply to or remove from the component you're looking to get.
More details about transformations below.
## Using Injector to customise forms
Forms in the React layer are built declaratively, using the `FormSchema` API. A component called `FormBuilderLoader` is given a URL to a form schema definition, and it populates itself with fields (both structural and data-containing) and actions to create the UI for the form. Each form is required to have an `identifier` property, which is used to create context for Injector when field components are fetched. This affords developers the opportunity provide very surgical customisations.
Forms in the React layer are built declaratively, using the `FormSchema` API. A component called
`FormBuilderLoader` is given a URL to a form schema definition, and it populates itself with fields
(both structural and data-containing) and actions to create the UI for the form. Each form is required
to have an `identifier` property, which is used to create context for Injector when field components
are fetched. This affords developers the opportunity provide very surgical customisations.
### Updating the form schema
Most behavioural and aesthetic customisations will happen via a mutation of the form schema. For this, we'll use the `updater.form.alterSchema()` function.
Most behavioural and aesthetic customisations will happen via a mutation of the form schema. For this,
we'll use the `updater.form.alterSchema()` function.
```js
Injector.transform(
@ -523,7 +579,9 @@ Injector.transform(
);
```
The `alterSchema()` function takes a callback, with an instance of `FormStateManager` (`form` in the above example) as a parameter. `FormStateMangaer` allows you to declaratively update the form schema API using several helper methods, including:
The `alterSchema()` function takes a callback, with an instance of `FormStateManager` (`form` in the
above example) as a parameter. `FormStateMangaer` allows you to declaratively update the form schema
API using several helper methods, including:
* `updateField(fieldName:string, updates:object)`
* `updateFields({ myFieldName: updates:object })`
@ -737,4 +795,4 @@ You could manipulate the action called by the originalReducer, there isn't an ex
## Using Injector to customise GraphQL queries
(coming soon)
(coming soon)