An open API service indexing awesome lists of open source software.

https://github.com/michalkvasnicak/react-apollo-graphql

Get rid of decorators and use Apollo GraphQL queries and mutations in the simple and readable way.
https://github.com/michalkvasnicak/react-apollo-graphql

apollo apollo-client component graphql react react-native

Last synced: about 1 year ago
JSON representation

Get rid of decorators and use Apollo GraphQL queries and mutations in the simple and readable way.

Awesome Lists containing this project

README

          

# react-apollo-graphql

[![npm](https://img.shields.io/npm/v/react-apollo-graphql.svg)](https://www.npmjs.com/package/react-apollo-graphql)
[![CircleCI](https://circleci.com/gh/michalkvasnicak/react-apollo-graphql/tree/master.svg?style=svg&circle-token=20c4fae1b9bd62446eec2d27c334b154c06efc9a)](https://circleci.com/gh/michalkvasnicak/react-apollo-graphql/tree/master)

![gzip size](http://img.badgesize.io/https://unpkg.com/react-apollo-graphql/dist/react-apollo-graphql.min.js?compression=gzip&label=gzip%20size)
![size](http://img.badgesize.io/https://unpkg.com/react-apollo-graphql/dist/react-apollo-graphql.min.js?label=size)
![module formats: umd, cjs, esm](https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20esm-green.svg)

This is opinionated replacement for `graphql` decorator from `react-apollo` package.

**Under development, API can change**

`npm install --save react-apollo-graphql`

It provides:

* simple error handling on the component level
* readable passing of queries' results to your component
* typed props and render props using flow type
* server side render

## Usage

### Using `` from `react-apollo`

```js
import GraphQL from 'react-apollo-graphql';
import ApolloClient from 'apollo-client';
import ApolloProvider from 'react-apollo';

} />

```

### Passing `ApolloClient` directly

```js
import GraphQL from 'react-apollo-graphql';
import ApolloClient from 'apollo-client';

} />
```

### Queries

In order to use define and use queries, one has to initialize them.

```js
// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, ObservableQuery } from 'react-apollo-graphql/lib/types';

const queries = {
// queryA will be resolved only once
queryA: (
client: ApolloClient,
props: Object
): ObservableQuery<{ id: number }> => client.watchQuery({
query: gql`{ id }`,
}),
// queryB will be resolved everytime the relevant props change
queryB: (
client: ApolloClient,
props: Object,
options: QueryInitializerOptions
): ObservableQuery<{ name: string }> => {
// add our function which will be called on every props change
options.hasVariablesChanged((currentProps, nextProps) => {
if (currentProps.name === nextProps.name) {
return false;
}

return { name: nextProps.name };
});

return client.watchQuery({
query: gql`query test($name: String!) { id(name: $name)}`,
variables: { name: props.name },
});
}
};

{
console.log(initializeQueries.queryA.data);
console.log(initializeQueries.queryA.loading);
console.log(initializeQueries.queryA.error);
console.log(initializeQueries.queryA.networkStatus);
console.log(initializeQueries.queryA.partial);
}}
/>
```

### Mutations

In order to define and use mutations, one has to provided initializers. Initializers are called on every render so you have current `props` available in the initializers.

```js
// @flow

import type { ApolloClient, QueryResult } from 'react-apollo-graphql/lib/types';

const mutations = {
registerUser: (
client: ApolloClient,
props: Object
) => (): Promise> => client.mutate({
mutation: gql`mutation registerUser($email: String!) { registerUser(email: $email) }`,
variables: {
email: props.email,
},
}),
};

{
mutations.registerUser(props.email).then(
(data) => console.log(data.registerUser),
e => console.error(e),
);
}}
/>
```

### Fetchers

In order to use fetchers (queries that run only when user invokes them), user has to first initialize them. Fetchers are initialized with `client` and current `props` on each render and passed to `render()` function.

```js
// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, QueryResult } from 'react-apollo-graphql/lib/types';

const fetchers = {
// queryA will be resolved only once
search: (
client: ApolloClient,
props: Object
) => (term: string): Promise>> => client.query({
query: gql`query search($term: String!) { search(term: $term) { id } }`,
variables: { term },
}),
};

{
fetchers.search(props.text).then(
(data) => console.log(data.search[0].id);
);
}}
/>
```

### Fragments

In order to use fragments (you can simulate partial results using fragments), user has to first initialize them. Fragments are initialized with `client`, previous `props` and current `props` on `componentWillMount` and every update if `props` used by given fragment have changed. If props have not changed and you don't want fragment to fetch data on every update, return `false`.

```js
// @flow

import type { QueryInitializerOptions } from 'react-apollo-graphql';
import type { ApolloClient, FragmentResult } from 'react-apollo-graphql/lib/types';

const fragments = {
// user detail will be resolved on componentWillMount and on every update if props
// used as variables have changed
userDetail: (
client: ApolloClient,
previousProps: ?Object,
currentProps: Object
): FragmentResult<{ __typename: 'User', id: number, name: string }> => {
if (previousProps && previousProps.id === currentProps.id) {
return false;
}

return client.readFragment({
id: `User:${currentProps.id}`,
fragment: gql`fragment userDetails on User { __typename, id, name }`,
});
}
};

{
expect(fragments.userDetail).toEqual({
__typename: 'User',
id: 10,
name: 'Fero',
});
}}
/>
```

## Server side render

For server side rendering you need to:

1. import helper as `import { getDataFromTree } from 'react-apollo-graphql';`
2. instantiate your view (`const view = ;`)
3. wait for all queries to be resolved `await getDataFromTree(view);`
4. render view `ReactDOM.renderToString(view);`
5. profit (but you have to hydrate your apollo store on the client side 😉 )

### React-Router v4

```js
// example taken from react-router v4 docs
import { createServer } from 'http';
import ApolloClient from 'apollo-client';
import ApolloProvider from 'react-apollo';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router';
import { getDataFromTree } from 'react-apollo-graphql';
import App from './App';

createServer(async (req, res) => {
const context = {};
const client = new ApolloClient();

const view = (





);

await getDataFromTree(view);

const html = ReactDOMServer.renderToString(view);

if (context.url) {
res.writeHead(301, {
Location: context.url
});
res.end();
} else {
res.write(`

${html}

`);
res.end();
}
}).listen(3000);
```

## API

### ApolloClient

* apollo client is provided from `apollo-client` package. See [documentation](http://dev.apollodata.com/core/apollo-client-api.html#apollo-client).

### QueryInitializerOptions

```js
// @flow

export type QueryInitializerOptions = {
// sets function to determine if there is a relevant change in props to compute new variables
// returns false if there is no change in props used for variables
// or returns new variables for query.setVariables()
hasVariablesChanged: (
(currentProps: Object, nextProps: Object) => boolean | { [key: string]: any },
) => void,
};
```

### ``

* `Fragments = { [key: string]: (client: ApolloClient, previousProps: ?Object, currentProps: Object) => FragmentResult }`
* `optional` prop, object with fragments' initializer
* **each initializer will be initialized with Apollo client, previousProps and props passed to the initializer on each mount and update**
* **each initializer is update only if it does not return false**
* each initializer has to return `false` or result of `client.readFragment()` (this means that it has to call the [`client.readFragment() method`](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.readFragment))
* `Fetchers = { [key: string]: (client: ApolloClient, props: Object) => (...args: any) => Promise>}`
* `optional` prop, object with fetchers' initializer
* **each initializer will be initialized with apollo client and props passed to the initializer on each render (on mount and every update)**
* each initializer has to return `(...args: any) => Promise>` (this means that it has to call the [`client.query() method`](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.query))
* `Queries = { [key: string]: (client: ApolloClient, props: Object, options: QueryInitializerOptions) => ObservableQuery<*> }`
* `optional` prop, object with query initializers.
* **each initializer will be initialized with apollo client and props passed to initializer on component mount**
* each initializer has to return `ObservableQuery` (this means that it has to call the [`client.watchQuery() method`](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.watchQuery))
* `Mutations = { [key: string]: (client: ApolloClient, props: Object) => () => Promise>}`
* `optional` prop, object with mutation initializers
* **each initializer will be initialized with apollo client and props passed to the initializer on each render (on mount and every update)**
* each initializer has to return `() => Promise>` (this means that it has to call the [`client.mutate() method`](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.mutate))
* `RenderFunction = (queries: InitializedQueries, mutations: InitializedMutations, fetchers: InitializedFetchers, props: Object) => React$Element`
* called on mount and updates
* `queries` arg: result of each query initializer passed to the `queries` prop on `` component will be mapped to it's result, plus additional methods like `fetchMore(), refetch(), etc` see [`client.watchQuery() method`](http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.watchQuery)
* `mutations` arg: each mutation initializer from the `mutations` prop passed to the `` component will be called on render and the result will be passed under the same `key` to the `mutations` arg of render function.
* `fetchers` arg: each fetcher initializer from the `fetchers` prop passed to the `` component will be called on render and the returned function will be passed under the same `key` to the `fetchers` arg of render function.
* `props` arg: current props passed to `` component