1 | # react-apollo-hooks
|
2 |
|
3 | Use [Apollo Client](https://github.com/apollographql/apollo-client) as React
|
4 | [hooks](https://reactjs.org/docs/hooks-intro.html).
|
5 |
|
6 | [![CircleCI](https://circleci.com/gh/trojanowski/react-apollo-hooks.svg?style=svg)](https://circleci.com/gh/trojanowski/react-apollo-hooks)
|
7 |
|
8 | # Installation
|
9 |
|
10 | `npm install react-apollo-hooks`
|
11 |
|
12 | # Example
|
13 |
|
14 | <https://codesandbox.io/s/8819w85jn9> is a port of Pupstagram sample app to
|
15 | react-apollo-hooks.
|
16 |
|
17 | # API
|
18 |
|
19 | ## ApolloProvider
|
20 |
|
21 | Similar to
|
22 | [ApolloProvider from react-apollo](https://www.apollographql.com/docs/react/essentials/get-started.html#creating-provider).
|
23 | Both packages can be used together, if you want to try out using hooks and
|
24 | retain `Query`, `Mutation`, `Subscription`, etc. HOCs from `react-apollo`
|
25 | without having to rewrite existing components throughout your app.
|
26 |
|
27 | In order for this package to work, you need to wrap your component tree with
|
28 | `ApolloProvider` at an appropriate level, encapsulating all components which
|
29 | will use hooks.
|
30 |
|
31 | ### Standalone usage
|
32 |
|
33 | If you would like to use this package standalone, this can be done with:
|
34 |
|
35 | ```javascript
|
36 | import React from 'react';
|
37 | import { render } from 'react-dom';
|
38 |
|
39 | import { ApolloProvider } from 'react-apollo-hooks';
|
40 |
|
41 | const client = ... // create Apollo client
|
42 |
|
43 | const App = () => (
|
44 | <ApolloProvider client={client}>
|
45 | <MyRootComponent />
|
46 | </ApolloProvider>
|
47 | );
|
48 |
|
49 | render(<App />, document.getElementById('root'));
|
50 | ```
|
51 |
|
52 | ### Usage with react-apollo
|
53 |
|
54 | To use with `react-apollo`'s `ApolloProvider` already present in your project:
|
55 |
|
56 | ```javascript
|
57 | import React from 'react';
|
58 | import { render } from 'react-dom';
|
59 |
|
60 | import { ApolloProvider } from 'react-apollo';
|
61 | import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks';
|
62 |
|
63 | const client = ... // create Apollo client
|
64 |
|
65 | const App = () => (
|
66 | <ApolloProvider client={client}>
|
67 | <ApolloHooksProvider client={client}>
|
68 | <MyRootComponent />
|
69 | </ApolloHooksProvider>
|
70 | </ApolloProvider>
|
71 | );
|
72 |
|
73 | render(<App />, document.getElementById('root'));
|
74 | ```
|
75 |
|
76 | ## useQuery
|
77 |
|
78 | ```javascript
|
79 | import gql from 'graphql-tag';
|
80 | import { useQuery } from 'react-apollo-hooks';
|
81 |
|
82 | const GET_DOGS = gql`
|
83 | {
|
84 | dogs {
|
85 | id
|
86 | breed
|
87 | }
|
88 | }
|
89 | `;
|
90 |
|
91 | const Dogs = () => {
|
92 | const { data, error, loading } = useQuery(GET_DOGS);
|
93 | if (loading) {
|
94 | return <div>Loading...</div>;
|
95 | };
|
96 | if (error) {
|
97 | return `Error! ${error.message}`;
|
98 | };
|
99 |
|
100 | return (
|
101 | <ul>
|
102 | {data.dogs.map(dog => (
|
103 | <li key={dog.id}>{dog.breed}</li>
|
104 | ))}
|
105 | </ul>
|
106 | );
|
107 | };
|
108 |
|
109 | ```
|
110 |
|
111 | ### Usage with Suspense (experimental)
|
112 |
|
113 | You can use `useQuery` with [React Suspense](https://www.youtube.com/watch?v=6g3g0Q_XVb4)
|
114 | with the `{ suspend: true }` option.
|
115 | Please note that it's not yet recommended to use it in production. Please look
|
116 | at the [issue #69](https://github.com/trojanowski/react-apollo-hooks/issues/69)
|
117 | for details.
|
118 |
|
119 | Example usage:
|
120 |
|
121 | ```javascript
|
122 | import gql from 'graphql-tag';
|
123 | import React, { Suspense } from 'react';
|
124 | import { useQuery } from 'react-apollo-hooks';
|
125 |
|
126 | const GET_DOGS = gql`
|
127 | {
|
128 | dogs {
|
129 | id
|
130 | breed
|
131 | }
|
132 | }
|
133 | `;
|
134 |
|
135 | const Dogs = () => {
|
136 | const { data, error } = useQuery(GET_DOGS, { suspend: true });
|
137 | if (error) {
|
138 | return `Error! ${error.message}`;
|
139 | }
|
140 |
|
141 | return (
|
142 | <ul>
|
143 | {data.dogs.map(dog => (
|
144 | <li key={dog.id}>{dog.breed}</li>
|
145 | ))}
|
146 | </ul>
|
147 | );
|
148 | };
|
149 |
|
150 | const MyComponent = () => (
|
151 | <Suspense fallback={<div>Loading...</div>}>
|
152 | <Dogs />
|
153 | </Suspense>
|
154 | );
|
155 | ```
|
156 |
|
157 | There are known issues with suspense mode for `useQuery`:
|
158 |
|
159 | * only the `cache-first` fetch policy is supported ([#13](https://github.com/trojanowski/react-apollo-hooks/issues/13))
|
160 | * `networkStatus` returned by `useQuery` is undefined ([#68](https://github.com/trojanowski/react-apollo-hooks/pull/68))
|
161 |
|
162 | ## useMutation
|
163 |
|
164 | ```javascript
|
165 | import gql from 'graphql-tag';
|
166 | import { useMutation } from 'react-apollo-hooks';
|
167 |
|
168 | const TOGGLE_LIKED_PHOTO = gql`
|
169 | mutation toggleLikedPhoto($id: String!) {
|
170 | toggleLikedPhoto(id: $id) @client
|
171 | }
|
172 | `;
|
173 |
|
174 | const DogWithLikes = ({ url, imageId, isLiked }) => {
|
175 | const toggleLike = useMutation(TOGGLE_LIKED_PHOTO, {
|
176 | variables: { id: imageId },
|
177 | });
|
178 | return (
|
179 | <div>
|
180 | <img src={url} />
|
181 | <button onClick={toggleLike}>{isLiked ? 'Stop liking' : 'like'}</button>
|
182 | </div>
|
183 | );
|
184 | };
|
185 | ```
|
186 |
|
187 | You can provide any
|
188 | [mutation options](https://www.apollographql.com/docs/react/api/apollo-client.html#ApolloClient.mutate)
|
189 | as an argument to the `useMutation` hook or to the function returned by it, e.
|
190 | g.:
|
191 |
|
192 | ```javascript
|
193 | function AddTaskForm() {
|
194 | const inputRef = useRef();
|
195 | const addTask = useMutation(ADD_TASK_MUTATION, {
|
196 | update: (proxy, mutationResult) => {
|
197 | /* your custom update logic */
|
198 | },
|
199 | variables: {
|
200 | text: inputRef.current.value,
|
201 | },
|
202 | });
|
203 |
|
204 | return (
|
205 | <form>
|
206 | <input ref={inputRef} />
|
207 | <button onClick={addTask}>Add task</button>
|
208 | </form>
|
209 | );
|
210 | }
|
211 | ```
|
212 |
|
213 | Or:
|
214 |
|
215 | ```javascript
|
216 | function TasksWithMutation() {
|
217 | const toggleTask = useMutation(TOGGLE_TASK_MUTATION);
|
218 |
|
219 | return (
|
220 | <TaskList
|
221 | onChange={task => toggleTask({ variables: { taskId: task.id } })}
|
222 | tasks={data.tasks}
|
223 | />
|
224 | );
|
225 | }
|
226 | ```
|
227 |
|
228 | ## useSubscription
|
229 |
|
230 | If you are just interested in the last subscription value sent by the
|
231 | server (e. g. a global indicator showing how many new messages you have in an
|
232 | instant messenger app) you can use `useSubscription` hook in this form:
|
233 |
|
234 | ```javascript
|
235 | const NEW_MESSAGES_COUNT_CHANGED_SUBSCRIPTION = gql`
|
236 | subscription onNewMessagesCountChanged($repoFullName: String!) {
|
237 | newMessagesCount
|
238 | }
|
239 | `;
|
240 |
|
241 | const NewMessagesIndicator = () => {
|
242 | const { data, error, loading } = useSubscription(
|
243 | NEW_MESSAGES_COUNT_CHANGED_SUBSCRIPTION
|
244 | );
|
245 |
|
246 | if (loading) {
|
247 | return <div>Loading...</div>;
|
248 | };
|
249 |
|
250 | if (error) {
|
251 | return <div>Error! {error.message}`</div>;
|
252 | };
|
253 |
|
254 | return <div>{data.newMessagesCount} new messages</div>;
|
255 | }
|
256 | ```
|
257 |
|
258 | For more advanced use cases, e. g. when you'd like to show a notification
|
259 | to the user or modify the Apollo cache (e. g. you'd like to show a new comment
|
260 | on a blog post page for a user visiting it just after it was created) you can
|
261 | use the `onSubscriptionData` callback:
|
262 |
|
263 | ```javascript
|
264 | const { data, error, loading } = useSubscription(MY_SUBSCRIPTION, {
|
265 | variables: {
|
266 | // ...
|
267 | },
|
268 | onSubscriptionData: ({ client, subscriptionData }) => {
|
269 | // Optional callback which provides you access to the new subscription
|
270 | // data and the Apollo client. You can use methods of the client to update
|
271 | // the Apollo cache:
|
272 | // https://www.apollographql.com/docs/react/advanced/caching.html#direct
|
273 | }
|
274 | // ... rest options
|
275 | });
|
276 | ```
|
277 |
|
278 |
|
279 | ## useApolloClient
|
280 |
|
281 | ```javascript
|
282 | const MyComponent = () => {
|
283 | const client = useApolloClient();
|
284 | // now you have access to the Apollo client
|
285 | };
|
286 | ```
|
287 |
|
288 | # Testing
|
289 |
|
290 | An example showing how to test components using react-apollo-hooks:
|
291 | <https://github.com/trojanowski/react-apollo-hooks-sample-test>
|
292 |
|
293 | # Server-side rendering
|
294 |
|
295 | react-apollo-hooks supports server-side rendering with the `getMarkupFromTree`
|
296 | function. Example usage:
|
297 |
|
298 | ```javascript
|
299 | import express from 'express';
|
300 | import { ApolloProvider, getMarkupFromTree } from 'react-apollo-hooks';
|
301 | import { renderToString } from 'react-dom/server';
|
302 |
|
303 | const HELLO_QUERY = gql`
|
304 | query HelloQuery {
|
305 | hello
|
306 | }
|
307 | `;
|
308 |
|
309 | function Hello() {
|
310 | const { data } = useQuery(HELLO_QUERY);
|
311 |
|
312 | return <p>{data.message}</p>;
|
313 | }
|
314 |
|
315 | const app = express();
|
316 |
|
317 | app.get('/', async (req, res) => {
|
318 | const client = createYourApolloClient();
|
319 | const renderedHtml = await getMarkupFromTree({
|
320 | renderFunction: renderToString,
|
321 | tree: (
|
322 | <ApolloProvider client={client}>
|
323 | <Hello />
|
324 | </ApolloProvider>
|
325 | ),
|
326 | });
|
327 | res.send(renderedHtml);
|
328 | });
|
329 | ```
|
330 |
|
331 | `getMarkupFromTree` supports `useQuery` hooks invoked in both suspense
|
332 | and non-suspense mode, but the [React.Suspense](https://reactjs.org/docs/react-api.html#reactsuspense)
|
333 | component is not supported. You can use `unstable_SuspenseSSR` provided
|
334 | by this library instead:
|
335 |
|
336 | ```javascript
|
337 | import { unstable_SuspenseSSR as UnstableSuspenseSSR } from 'react-apollo-hooks';
|
338 |
|
339 | function MyComponent() {
|
340 | return (
|
341 | <UnstableSuspenseSSR fallback={<Spinner />}>
|
342 | <div>
|
343 | <ComponentWithGraphqlQuery />
|
344 | </div>
|
345 | </UnstableSuspenseSSR>
|
346 | );
|
347 | }
|
348 | ```
|