React's job is to render UI. Its UI elements are known as "components" and represent the fundamental building block of a React-rendered interface. A React component expressed like this:
This syntax is known as JSX. It is transpiled at build time into native Javascript calls
to the React API. While optional, it is recommended to express components this way.
### Recommended: React Dev Tools
The [React Dev Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) extension available for Chrome and Firefox is critical to debugging a React UI. It will let you browse the React UI much like the DOM, showing the tree of rendered components and their current props and state in real time.
## Redux
Redux is a state management tool with a tiny API that affords the developer highly predictable behaviour. All of the application state is stored in a single object, and the only way to mutate that object is by calling an action, which is just a simple object that describes what happened. A function known as a _reducer_ mutates the state based on that action and returns a new reference with the updated state.
The following example is taken from the [Redux Github page](https://github.com/reactjs/redux):
It's important to be able to view the state of the React application when you're debugging and
building the interface.
To be able to view the state, you'll need to be in a dev environment
and have the [Redux Devtools](https://github.com/zalmoxisus/redux-devtools-extension)
installed on Google Chrome or Firefox, which can be found by searching with your favourite search
engine.
## GraphQL and Apollo
[GraphQL](http://graphql.org/learn/) is a strictly-typed query language that allows you to describe what data you want to fetch from your API. Because it is based on types, it is self-documenting and predictable. Further, it's structure lends itself nicely to fetching nested objects. Here is an example of a simple GraphQL query:
The above query is almost self-descriptive. It gets a user by ID, returns his or her name and email address, along with the title of any blog posts he or she has written, and the first five comments for each of those. The result of that query is, very predictably, JSON that takes on the same structure.
On its own, GraphQL offers nothing functional, as it's just a query language. You still need a service that will invoke queries and map their results to UI. For that, SilverStripe uses an implementation of [Apollo](http://dev.apollodata.com/) that works with React.
## For more information
This documentation will stop short of explaining React, Redux, and GraphQL/Apollo in-depth, as there is much better
documentation available all over the web. We recommend:
* [The Official React Tutorial](https://facebook.github.io/react/tutorial/tutorial.html)
* [Build With React](http://buildwithreact.com/tutorial)
* [Getting Started with Redux](https://egghead.io/courses/getting-started-with-redux)
* [The React Apollo docs](http://dev.apollodata.com/react/)
# The Injector API
Much like SilverStripe's [Injector API](../../extending/injector) in PHP,
the client-side framework has its own implementation of dependency injection
known as `Injector`. Using Injector, you can register new services, and
transform existing services.
Injector is broken up into three sub-APIs:
*`Injector.component` for React UI components
*`Injector.reducer` for Redux state management
*`Injector.form` for forms rendered via `FormSchema`.
The frontend Injector works a bit differently than its backend counterpart. Instead of _overriding_ a service with your own implementation, you _enhance_ an existing service with your own concerns. This pattern is known as [middleware](https://en.wikipedia.org/wiki/Middleware).
Middleware works a lot like a decorator. It doesn't alter the original API of the service,
but it can augment it with new features and concerns. This has the inherent advantage of allowing all thidparty code to have an influence over the behaviour, state, and UI of a component.
## A simple middleware example
Let's say you have an application that features error logging. By default, the error logging service simply outputs to `console.error`. But you want to customise it to send errors to a thirdparty service. For this, you could use middleware to augment the default functionality of the logger.
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';
We haven't overriden any functionality. `LoggingService(error)` will still invoke `console.error`, once all the middleware has run. But what if we did want to kill the original functionality?
```js
import LoggingService from './LoggingService';
import thirdPartyLogger from 'third-party-logger';
Much like the configuration layer, we need to specify a name for this transformation. This will help other modules negotiate their priority over the injector in relation to yours.
The second parameter of the `transform` argument is a callback which receives an `updater`object. It contains four functions: `component()`, `reducer()`, `form.alterSchema()` and `form.addValidation()`. We'll cover all of these in detail functions in detail further into the document, but briefly, these update functions allow you to mutate the DI container with a wrapper for the service. Remember, this function does not _replace_
the service -- it enhances it with new functionality.
### Helpful tip: Name your component middleware
Since multiple enhancements can be applied to the same component, it will be really
useful for debugging purposes to reveal the names of each enhancement on the `displayName` of
the component. This will really help you when viewing the rendered component tree in
[React Dev Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en).
For this, you can use the third parameter of the `updater.component` function. It takes an arbitrary
Sometimes, it's critical to ensure that your customisation happens after another one has been executed. To afford you control over the ordering of transforms, Injector allows `before` and `after` attributes as metadata for the transformation.
Because so much of UI design depends on context, dependency injection in the frontend is not necessarily universal. Instead, services are fetched with context.
To apply context-based transformations, you'll need to know the context of the component you want to customise. To learn this,
open your React Developer Tools (see above) window and inspect the component name. The
context of the component is displayed between two square brackets, appended to the original name, for example:
`TextField[TextField.AssetAdmin.FileEditForm.Title]`. The context description is hierarchical, starting
with the most general category (in this case, "Admin") and working its way down to the most specific
category (Name = 'Title'). You can use Injector to hook into the level of specificity that you want.
# Customising React components with Injector
When middleware is used to customise a React component, it is known as a [higher order component](https://facebook.github.io/react/docs/higher-order-components.html).
Using the `PhotoItem` example above, let's create a customised `PhotoItem` that allows a badge, perhaps indicating that it is new to the gallery.
Validation for React-rendered forms is handled by the [redux-form](http://redux-form.com) package. You can inject your own middleware to add custom validation rules using the `updater.form.addValidation()` function.
The `addValidation()` function takes a callback, with an instance of `FormValidationManager` (`validator` in the above example) as a parameter. `FormValidationMangaer` allows you to manage the validation result using several helper methods, including:
Before starting this tutorial, you should become familiar with the concepts of [Immutability](https://www.sitepoint.com/immutability-javascript/) and [Redux](http://redux.js.org).
The examples use [Spread in object literals](http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html) which is at this moment in Stage 3 Proposal. If you're more comfortable with using
the `Object.assign()` API that shouldn't present any problems and should work the same.
For example:
```js
newProps = { ...oldProps, name: 'New name' };
```
is the same as
```js
newProps = Object.assign(
{},
oldProps,
{ name: 'New name' }
);
```
To start customising, you'll need to transform an existing registered reducer, you can find what reducers are registered by importing Injector and running `Injector.reducer.getAll()`
As you can see, we use the `reducer()` function on the `update` object to augment Redux state transformations.
### Using Redux dev tools
It is important to learn the basics of [Redux dev tools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en), so that you can find out what ACTIONS and payloads to intercept and modify in your Transformer should target.
Most importantly, it helps to understand the "Action" sub-tab on the right panel (bottom if your dev tools is small), as this will be the data your Transformer will most likely receive, pending other transformers that may run before/after your one.
### Structuring a transformer
We use currying to supply utilities which your transformer may require to handle the transformation.
-`originalReducer` - reducer callback which the transformer is customising, this will need to be called in most cases. This will also callback other transformations down the chain of execution. Not calling this will break the chain.
-`getGlobalState` - A function that gets the state of the global Redux store. There may be data outside the current scope in the reducer which you may need to help determine the transformation.
We can easily define a new initial state by providing the `state` param with a default value.
It is recommended to keep the call for the original initialState for your initialState then override values, so that you do not lose any potentially critical data that would have originally been set.
One of the strengths of GraphQL is that it allows us to declaratively state exactly what data a given component needs to function. Because GraphQL queries and mutations are considered primary concerns of a component, they are not abstracted away somewhere in peripheral asynchronous functions. Rather, they are co-located with the component definition itself.
The downside of this is that, because queries are defined statically at compile time, they don't adapt well to the extension patterns that are inherent to SilverStripe projects. For instance, a query for a Member record may include fields for `FirstName` and `Email`, but if you have customised that class via extensions, and would like the component using that query to display your custom fields, your only option is to override the entire query and the component with a custom implementation. In backend code, this would be tantamount to replacing the entire `Member` class and `SecurityAdmin` section just because you had a new field. You would never do that, right? It's an over-aggressive hack! We need APIs that make extension easy.
To that end, the `Injector` library provides a container for abstract representations of GraphQL queries and mutations. You can register and transform them as you do components and reducers. They exist merely as abstract concepts until `Injector` loads, at which time all transformations are applied, and each registered query and mutation is composed and attached to their assigned components.
### Extensions are only as good as the code they're extending
An important point to remember about these types of deep customisations is that they all depend heavily on the core code they're modifying to follow specific patterns. The more the core code makes use of `Injector` the easier it will be for third party developers to extend. Conversely, if the core is full of hard-coded component definitions and statically written queries, customisation will be at best less surgical and at worst, not possible. For this reason, we'll look at GraphQL customisations from two sides -- making code extensible, and then extending that code.
### Building an extensible GraphQL app
Let's imagine that we have a module that adds a tab where the user can write "notes" about the content he or she is editing. We'll use GraphQL and React to render this UI. We have a dataobject called "Note" where we store these in the database.
What we've just built may work, but we've made life very difficult for other developers. They have no way of customising this. Let's change that.
#### Register as much as possible with Injector
The best thing you can do to make your code extensible is to use `Injector` early and often. Anything that goes through Injector is easily customisable.
First, let's break up the list into smaller components.
The next piece is the query. We'll need to register that with `Injector`. Unlike components and reducers, this is a lot more abstract. We're actually not going to write any GraphQL at all. We'll just build the concept of the query in an abstraction layer, and leave `Injector` to build the GraphQL syntax at runtime.
Dynamic GraphQL queries are generated by populating pre-baked templates with specific pieces of data, including fields, fragments, variables, parameters, and more. By default, the templates available to you follow the GraphQL scaffolding API (`readMyObjects`, `readOneMyObject`, `createMyObject`, `deleteMyObject`, and `updateMyObject`).
In this example, we're using the `READ` template, which needs to know the plural name of the object (e.g. `readNotes`), whether pagination is activated, and which fields you want to query.
#### Register all the things
Let's now register all of this with Injector.
*myapp/client/registerDependencies.js*
```js
import NotesList from './NotesList';
import NotesListItem from './NotesListItem';
import readNotes from './readNotes';
import Injector, { injectGraphql } from 'lib/Injector';
The only missing piece now is to attach the `ReadNotes` injected query to the `NotesList` component. We could have done this using `injectGraphql` in the `NotesList` component itself, but instead, we'll do it as an Injector transformation. Why? There's a good chance whoever is customising the query will want to customise the UI of the component that is using that query. If someone adds a new field to a query, it is likely the component should display that new field. Registering the GraphQL injection as a transformation will allow a thirdparty developer to override the UI of the component explicitly *after* the GraphQL query is attached. This is important, because otherwise, the component override would not be wired up to your data source.
The transformation adds the higher-order component `injectGraphQL`, using the query we have just registered, `ReadNotes` as a dependency.
All of this feels like a lot of extra work, and, to be fair, it is. You're probably used to simply inlining one or many higher-order component compositions in your components. That works great when you're not concerned about making your components extensible, but if you want others to be able to customise your app, you really need to be sure to follow these steps.
#### Define the app
Let's make a really simple container app that calls `NotesList` as a dependency.
*myapp/client/App.js*
```js
import React from 'react';
import { inject } from 'lib/Injector';
const App = ({ ListComponent }) => (
<div>
<h3>Notes</h3>
<ListComponent/>
</div>
);
export default inject(
['NotesList'],
(NotesList) => ({
ListComponent: NotesList,
})
)(App);
```
You can register this with `Injector`, too, but since it's already injected with dependencies, it could get pretty convoluted. High level components like this are best left uncustomisable.
We register a callback with `Injector` to ensure that we don't attempt to render anything before the transformations have been applied. This would result in fatal errors, so be sure to wrap your mounting code in `Injector.ready()`.
The `silverstripe/admin` module was generous enough to hang its `apolloClient` and `store` objects in the global namespace to be shared by other modules. We'll make use of those, and create our own app wrapped in `<ApolloProvider />`. We then make our app `Injector` aware by wrapping it with the `provideInjector` higher-order component.
To mount the app, we use the `onmatch()` event fired by entwine, and we're off and running.
### Extending an existing GraphQL app
Let's suppose we have our own module that extends the `Notes` object in some way. Perhaps we have a `Priority` field whose value alters the UI in some way. Thanks to a module developer who gave use plenty of extension points through `Injector`, this will be pretty easy.
#### Applying the extensions
We'll first need to apply the extension and update our GraphQL scaffolding.
Simple! The transformation passes us a `ApolloGraphQLManager` instance that provides a fluent API for updating a query definition the same way the `FormStateManager` allows us to update Redux form state.
#### Adding fields
In the above example, we added a single field to a query. Here's how that works:
The `fieldPath` argument tells the manager at what level to add the field. In this case, since the `Priority` field is going on the root query (`readNotes`), we'll use `root` as the path. But suppose we had a more complex query like this:
```
query readMembers {
FirstName
Surname
Friends {
Email
Company {
Name
}
}
}
```
If we wanted to add a field to the nested `Company` query on `Friends`, we would use a path syntax.
It looks like a lot of code, but if you're familiar with Apollo mutations, this is pretty standard. The supplied `mutate` function gets mapped to a prop -- in this case `onAdd`, which the `AddForm` component is configured to invoke. We've also supplied the `singularName` as well as the template `CREATE` for the `createNote` scaffolded mutation.
Lastly, let's just register all this with `Injector`.
This is exactly the same pattern as we did before with a query, only with different components and GraphQL abstractions this time. Note that even though `CreateNote` is a mutation, it still gets registered under `Injector.query` for simplicity.
### Extending mutations
Now let's switch back to our thirdparty module that is customising our Notes application. The developer is going to want to ensure that users can supply a "Priority" value for each note entered. This will involve updating the `AddForm` component.
*my-other-app/client/transformAddForm.js*
```js
import React from 'react';
const transformAddForm = () => ({ onAdd }) => {
let content, priority;
return (
<div>
<label>Note content</label>
<inputtype="text"ref={node=> content = node}/>
<label>Priority</label>
<selectref={node=> priority = node}>
<optionvalue="low">Low</option>
<optionvalue="medium">Medium</option>
<optionvalue="high">High</option>
</select>
<buttononClick={(e)=> {
e.preventDefault();
if (content && priority) {
onAdd(content.value, priority.value);
}
}}>Add</button>
</div>
);
};
export default transformAddForm;
```
We've extended the `onAdd` callback to take two parameters -- one for the note content, and another for the priority. We'll need to update the mutation to reflect this.
All we've done here is overridden the `props` setting in the `CreateNote` apollo config. Recall from the previous section that it maps the `mutate` function to the `onAdd` prop. Since we've changed the signature of that function, we'll need to override the entire prop.
Now we just need to register these transforms, and we're done!
```js
//...
import transformAddForm from './transformAddForm';