Warning: master branch is currently out of sync with npm. Switch to the 0.11.1 tag to see previous docs!
This library provides subset of Relay behaviour with a cleaner API.
Relay is a great framework with exciting ideas behind it. The downside is that in order to get all cool features of one you need to deal with complex API. Relay provides you a lot of tricky optimizations which probably are more suitable for huge projects. In small, medium and even large ones you would prefer to have better developer experience while working with a simple minimalistic set of APIs.
Adrenaline intend to provide you Relay-like ability to describe your components with declarative data requirements, while keeping API as simple as possible. You are free to use it with different libraries like Redux, React Router and etc.
- You have a huge project and highly need tricky optimisations to reduce client-server traffic.
- When you don't understand why should you prefer Adrenaline to Relay.
npm install --save adrenaline@1.0.0-rc1
Adrenaline requires React 0.14 or later.
Adrenaline uses fetch
under the hood so you need to install polyfill by yourself
npm install --save whatwg-fetch
and then import it at the very top of your entry JavaScript file
import 'whatwg-fetch';
import React from 'react';
import ReactDOM from 'react-dom';
import { Adrenaline } from 'adrenaline';
import App from './components/App';
ReactDOM.render(
<Adrenaline>
<App />
</Adrenaline>,
document.getElementById('root')
)
Adrenaline follows the idea of Presentational and Container Components
Root of your application should be wrapped with Adrenaline component. This component is a provider component which injects some helpful stuff in your React application.
prop name | type | default/required | purpose |
---|---|---|---|
endpoint | string | "/graphql" | URI of your GraphQL endpoint |
In Adrenaline you would create container components mostly for your route handlers. Purpose of containers is to collect data requirements from presentation components in a single GraphQL query. Also they behave like view controllers and are able to speak to outside world using mutations.
key | type | default/required | purpose |
---|---|---|---|
variables | (props: Props) => Object | () => ({}) | describe query variables as a pure function of props |
query | string | required | your GraphQL query for this container |
import React, { Component, PropTypes } from 'react';
import { container } from 'adrenaline';
import TodoList from './TodoList';
class UserItem extends Component {
static propTypes = {
viewer: PropTypes.object.isRequired,
}
/* ... */
}
export default container({
variables: (props) => ({
id: props.userId,
}),
query: `
query ($id: ID!) {
viewer(id: $id) {
id,
name,
${TodoList.getFragment('todos')}
}
}
`,
})(UserItem);
Also container would pass 2 additional properties to your component mutate
and isFetching
.
mutate({ mutation: String, variables: Object = {}, invalidate: boolean = true }): Promise
: You need to use this function in order to perform mutations.invalidate
argument means you need to resolve data declarations after mutation.isFetching: boolean
: This property helps you understand if your component is in the middle of resolving data.
As in presentational components idea all your dumb components may be declared as simple React components. But if you want to declare your data requirements in similar to Relay way you can use createDumbComponent
function.
import React, { Component } from 'react';
import { presenter } from 'adrenaline';
class TodoList extends Component {
/* ... */
}
export default presenter({
fragments: {
todos: `
fragment on User {
todos {
id,
text
}
}
`,
},
})(TodoList);
You can declare your mutations as simple as
const createTodo = `
mutation ($text: String, $owner: ID) {
createTodo(text: $text, owner: $owner) {
id,
text,
owner {
id
}
}
}
`;
Then you can use this mutation with your component
import React, { Component, PropTypes } from 'react';
import { createSmartComponent } from 'adrenaline';
class UserItem extends Component {
static propTypes = {
viewer: PropTypes.object.isRequired,
}
onSomeButtonClick() {
this.props.mutate({
mutation: createTodo,
variables: {
text: hello,
owner: this.props.viewer.id
},
});
}
render() {
/* render some stuff */
}
}
There is a common problem I've discovered so far while developing applications. When you change GraphQL schema you'd like to know which particular subtrees in your applications need to be fixed. And you probably do not want to check this running your application and go through it by hands.
For this case Adrenaline provides you helper utilities for integration testing. Currently for expect
only. You can use toBeValidAgainst
for checking your components data requirements against your schema with GraphQL validation mechanism.
import expect from 'expect';
import TestUtils from 'adrenaline/lib/test';
import schema from 'path/to/schema';
// TodoApp is a container component
import TodoApp from 'path/to/TodoApp';
expect.extend(TestUtils.expect);
describe('Queries regression', () => {
it('for TodoApp', () => {
expect(TodoApp).toBeValidAgainst(schema);
});
});