-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
quick-start-guide.html
520 lines (482 loc) · 47.8 KB
/
quick-start-guide.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Quick Start Guide · Relay</title><meta name="viewport" content="width=device-width"/><meta name="generator" content="Docusaurus"/><meta name="description" content="In this guide we are going to give a brief overview of how Relay works and how to use it, using as reference an example todo list app. For more thorough documentation, check out our Guides and API sections."/><meta name="docsearch:version" content="classic"/><meta name="docsearch:language" content="en"/><meta property="og:title" content="Quick Start Guide · Relay"/><meta property="og:type" content="website"/><meta property="og:url" content="https://relay.dev/"/><meta property="og:description" content="In this guide we are going to give a brief overview of how Relay works and how to use it, using as reference an example todo list app. For more thorough documentation, check out our Guides and API sections."/><meta property="og:image" content="https://relay.dev/img/relay.png"/><meta name="twitter:card" content="summary"/><meta name="twitter:image" content="https://relay.dev/img/relay.png"/><link rel="shortcut icon" href="/img/favicon.png"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css"/><script type="text/javascript" src="/js/redirect.js"></script><script src="https://unpkg.com/vanilla-back-to-top@7.1.14/dist/vanilla-back-to-top.min.js"></script><script>
document.addEventListener('DOMContentLoaded', function() {
addBackToTop(
{"zIndex":100}
)
});
</script><script src="/js/scrollSpy.js"></script><link rel="stylesheet" href="/css/main.css"/><script src="/js/codetabs.js"></script></head><body class="sideNavVisible separateOnPageNav"><div class="fixedHeaderContainer"><div class="headerWrapper wrapper"><header><a href="/en"><h2 class="headerTitle">Relay</h2></a><a href="/en/versions"><h3>classic</h3></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li class=""><a href="/docs/en/classic/introduction-to-relay" target="_self">Docs</a></li><li class=""><a href="/en/help" target="_self">Help</a></li><li class="navSearchWrapper reactNavSearchWrapper"><input type="text" id="search_input_react" placeholder="Search" title="Search"/></li><li class=""><a href="https://github.com/facebook/relay" target="_self">GitHub</a></li><li class=""><a target="_self"></a></li></ul></nav></div></header></div></div><div class="navPusher"><div class="docMainWrapper wrapper"><div class="container mainContainer docsContainer"><div class="wrapper"><div class="post"><header class="postHeader"><a class="edit-page-link button" href="https://github.com/facebook/relay/edit/master/docs/Introduction-QuickStartGuide.md" target="_blank" rel="noreferrer noopener">Edit</a><h1 id="__docusaurus" class="postHeaderTitle">Quick Start Guide</h1></header><article><div><span><p>In this guide we are going to give a brief overview of how Relay works and how to use it, using as reference an example todo list app. For more thorough documentation, check out our Guides and API sections.</p>
<p>Table of Contents:</p>
<ul>
<li><a href="#setup">Setup</a></li>
<li><a href="#relay-environment">Relay Environment</a></li>
<li><a href="#rendering-graphql-queries">Rendering GraphQL Queries</a></li>
<li><a href="#using-query-variables">Using Query Variables</a></li>
<li><a href="#using-fragments">Using Fragments</a></li>
<li><a href="#composing-fragments">Composing Fragments</a></li>
<li><a href="#rendering-fragments">Rendering Fragments</a></li>
<li><a href="#mutating-data">Mutating Data</a></li>
<li><a href="#next-steps">Next Steps</a></li>
</ul>
<h2><a class="anchor" aria-hidden="true" id="setup"></a><a href="#setup" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Setup</h2>
<p>Before starting, make sure to check out our <a href="./prerequisites.html">Prerequisites</a> and <a href="./installation-and-setup.html">Installation and Setup</a> guides. As mentioned in the prerequisites, we need to make sure that we've set up a GraphQL server and schema.</p>
<p>Fortunately, we are going to be using this <a href="https://github.com/relayjs/relay-examples/tree/master/todo">example todo list app</a>, which already has a <a href="https://github.com/relayjs/relay-examples/blob/master/todo/server.js">server</a> and schema <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql">schema</a> available for us to use:</p>
<pre><code class="hljs css language-graphql"><span class="hljs-comment"># From schema.graphql</span>
<span class="hljs-comment"># https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql</span>
<span class="hljs-built_in">
type </span>Query {
viewer:<span class="hljs-built_in"> User
</span>
# Fetches an object given its ID
node(
# The ID of an object
id: ID!
): Node
}
</code></pre>
<p>Additionally, we will be using <a href="https://flow.org/">Flow</a> inside our JavaScript code examples. Flow is optional to set up in your project, but we will include it in our examples for completeness.</p>
<h2><a class="anchor" aria-hidden="true" id="relay-environment"></a><a href="#relay-environment" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Relay Environment</h2>
<p>Before we can start rendering pixels on the screen, we need to configure Relay via a <a href="./relay-environment.html">Relay Envionment</a>. The environment bundles together the configuration, cache storage, and network-handling that Relay needs in order to operate.</p>
<p>For the purposes of our example, we are simply going to configure our environment to communicate with our existing GraphQL server:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">import</span> {
Environment,
Network,
RecordSource,
Store,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'relay-runtime'</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchQuery</span>(<span class="hljs-params">
operation,
variables,
</span>) </span>{
<span class="hljs-keyword">return</span> fetch(<span class="hljs-string">'/graphql'</span>, {
<span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
<span class="hljs-attr">headers</span>: {
<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
},
<span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
<span class="hljs-attr">query</span>: operation.text,
variables,
}),
}).then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> {
<span class="hljs-keyword">return</span> response.json();
});
}
<span class="hljs-keyword">const</span> environment = <span class="hljs-keyword">new</span> Environment({
<span class="hljs-attr">network</span>: Network.create(fetchQuery),
<span class="hljs-attr">store</span>: <span class="hljs-keyword">new</span> Store(<span class="hljs-keyword">new</span> RecordSource()),
});
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> environment;
</code></pre>
<p>A Relay Environment requires at least a <a href="./relay-store.html">Store</a> and a <a href="./network-layer">Network Layer</a>. The above code uses the default implementation for <code>Store</code>, and creates a <a href="./network-layer">Network Layer</a> using a simple <code>fetchQuery</code> function to fetch a GraphQL query from our server.</p>
<p>Usually we'd want a single environment in our app, so you could export this environment as a singleton instance from a module to make it accessible across your app.</p>
<h2><a class="anchor" aria-hidden="true" id="rendering-graphql-queries"></a><a href="#rendering-graphql-queries" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Rendering GraphQL Queries</h2>
<p>Now that we've configured our Relay Environment, we can start fetching queries and rendering data on the screen. The entry point to render data from a GraphQL query is the <a href="./query-renderer"><code>QueryRenderer</code></a> component provided by <code>react-relay</code>.</p>
<p>To start, let's assume we just want to render the user id on the screen. From our <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql#L66">schema</a>, we know that we can get the current <code>User</code> via the <code>viewer</code> field, so let's write a sample query to fetch the current user id:</p>
<pre><code class="hljs css language-graphql"><span class="hljs-attribute">query</span> UserQuery {
<span class="hljs-section">viewer</span> {
<span class="hljs-attribute">id</span>
}
}
</code></pre>
<p>Now, let's see what it would take to create a component that fetches and renders the above query:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// App.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {graphql, QueryRenderer} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>;
<span class="hljs-keyword">const</span> environment = <span class="hljs-comment">/* defined or imported above... */</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App extends React.Component {
render() {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">QueryRenderer</span>
<span class="hljs-attr">environment</span>=<span class="hljs-string">{environment}</span>
<span class="hljs-attr">query</span>=<span class="hljs-string">{graphql</span>`
<span class="hljs-attr">query</span> <span class="hljs-attr">UserQuery</span> {
<span class="hljs-attr">viewer</span> {
<span class="hljs-attr">id</span>
}
}
`}
<span class="hljs-attr">variables</span>=<span class="hljs-string">{{}}</span>
<span class="hljs-attr">render</span>=<span class="hljs-string">{({error,</span> <span class="hljs-attr">props</span>}) =></span> {
if (error) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Error!<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
if (!props) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Loading...<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>User ID: {props.viewer.id}<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}}
/></span>
);
}
}
</code></pre>
<p>Our app is rendering a <code>QueryRenderer</code> in the above code, like any other React Component, but let's see what's going on in the props that we are passing to it:</p>
<ul>
<li>We're passing the <code>environment</code> we defined earlier.</li>
<li>We're using using the <a href="./graphql-in-relay.html"><code>graphql</code></a> function to define our GraphQL query. <code>graphql</code> is a template tag that is never executed at runtime, but rather used by the <a href="./graphql-in-relay.html#relay-compiler">Relay Compiler</a> to generate the runtime artifacts that Relay requires to operate. We don't need to worry about this right now; for more details check out our <a href="./graphql-in-relay.html">GraphQL in Relay</a> docs.</li>
<li>We're passing an empty set of <code>variables</code>. We'll look into how to use variables in the next section.</li>
<li>We're passing a <code>render</code> function; as you can tell from the code, Relay gives us some information about wether an error occurred, or if we're still fetching the query. If everything succeeds, the data we requested will be available inside <code>props</code>, with the same shape as the one specified in the query.</li>
</ul>
<p>In order to run this app, we need to first compile our query using the Relay Compiler. Assuming the setup from <a href="./installation-and-setup">Installation and Setup</a>, we can just run <code>yarn relay</code>.</p>
<p>For more details on <code>QueryRenderer</code>, check out the <a href="./query-renderer">docs</a>.</p>
<h2><a class="anchor" aria-hidden="true" id="using-query-variables"></a><a href="#using-query-variables" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using Query Variables</h2>
<p>Let's assume for a moment that in our app we want to be able to view data for different users, so we're going to somehow need to query users by id. From our <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql#L69">schema</a>, we know we can query nodes given an id, so let's write a parametrized query to get a user by id:</p>
<pre><code class="hljs css language-graphql">query UserQuery(<span class="hljs-variable">$userID:</span> ID!) {
<span class="hljs-type">node</span><span class="hljs-built_in">(id</span>: <span class="hljs-variable">$userID</span>) {
<span class="hljs-built_in"> id</span>
}
}
</code></pre>
<p>Now, let's see how we would fetch the above query using a <code>QueryRenderer</code>:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// UserTodoList.js</span>
<span class="hljs-comment">// @flow</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">'prop-types'</span>;
<span class="hljs-keyword">import</span> {graphql, QueryRenderer} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>;
<span class="hljs-keyword">const</span> environment = <span class="hljs-comment">/* defined or imported above... */</span>;
type Props = {
<span class="hljs-attr">userID</span>: string,
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> UserTodoList extends React.Component<Props> {
render() {
<span class="hljs-keyword">const</span> {userID} = <span class="hljs-keyword">this</span>.props;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">QueryRenderer</span>
<span class="hljs-attr">environment</span>=<span class="hljs-string">{environment}</span>
<span class="hljs-attr">query</span>=<span class="hljs-string">{graphql</span>`
<span class="hljs-attr">query</span> <span class="hljs-attr">UserQuery</span>($<span class="hljs-attr">userID:</span> <span class="hljs-attr">ID</span>!) {
<span class="hljs-attr">node</span>(<span class="hljs-attr">id:</span> $<span class="hljs-attr">userID</span>) {
<span class="hljs-attr">id</span>
}
}
`}
<span class="hljs-attr">variables</span>=<span class="hljs-string">{{userID}}</span>
<span class="hljs-attr">render</span>=<span class="hljs-string">{({error,</span> <span class="hljs-attr">props</span>}) =></span> {
if (error) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Error!<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
if (!props) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Loading...<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>User ID: {props.node.id}<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}}
/></span>
);
}
}
</code></pre>
<p>The above code is doing something very similar to our <a href="#rendering-graphql-queries">previous example</a>, however, we are now passing a <code>$userID</code> variable to the GraphQL query, via the <code>variables</code> prop. This has a couple of important implications:</p>
<ul>
<li>Given that <code>userID</code> is also a prop that our component takes, it could receive a new <code>userID</code> from its parent component at any moment. When this happens, we new <code>variables</code> will be passed down to our <code>QueryRenderer</code>, which will automatically cause it to re-fetch the query with the new value for <code>$userID</code>.</li>
<li>The <code>$userID</code> variable will now be available anywhere inside that query; this will become important when to keep in mind when using fragments.</li>
</ul>
<p>Now that we've updated the query, don't forget to run <code>yarn relay</code>.</p>
<h2><a class="anchor" aria-hidden="true" id="using-fragments"></a><a href="#using-fragments" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using Fragments</h2>
<p>Now that we know how to define and fetch queries, let's actually start building a todo list.</p>
<p>First, let's start at the bottom; let's say that we want to render a component that given a todo item, simply displays the item's text and completed state:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// Todo.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
type Props = {
<span class="hljs-attr">todo</span>: {
<span class="hljs-attr">complete</span>: boolean,
<span class="hljs-attr">text</span>: string,
},
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span><<span class="hljs-title">Props</span>> </span>{
render() {
<span class="hljs-keyword">const</span> {complete, text} = <span class="hljs-keyword">this</span>.props.todo;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">li</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">input</span>
<span class="hljs-attr">checked</span>=<span class="hljs-string">{complete}</span>
<span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
/></span>
<span class="hljs-tag"><<span class="hljs-name">label</span>></span>
{text}
<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">li</span>></span></span>
);
}
}
</code></pre>
<p>From our <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql#L107">schema</a>, we know that we can query this data on the <code>Todo</code> type. However, we don't want to have to send a separate query for each todo item; that would defeat the purpose of using GraphQL over a traditional REST API. We could manually query for these fields directly in our <code>QueryRenderer</code> query, but that would hurt re-usability: what if we want to query the same set of fields as part of a different query? Additionally, we wouldn't know which component needs the data we're querying, a problem Relay directly tries to address.</p>
<p>Instead, we can define a reusable <a href="http://graphql.org/learn/queries/#fragments">Fragment</a>, which allows us to define a set of fields on a type and reuse them within our queries wherever we need to:</p>
<pre><code class="hljs css language-graphql">fragment TodoItemFragment <span class="hljs-keyword">on</span> Todo {
complete
<span class="hljs-built_in">text</span>
}
</code></pre>
<p>Our component can then use this fragment to declare its data dependency on the <code>Todo</code> GraphQL type:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// Todo.js</span>
<span class="hljs-comment">// OPTIONAL: Flow type generated after running `yarn relay`, defining an Object type with shape of the fragment:</span>
<span class="hljs-keyword">import</span> type {Todo_todo} <span class="hljs-keyword">from</span> <span class="hljs-string">'./__generated__/Todo_todo.graphql'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {graphql, createFragmentContainer} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>
type Props = {
<span class="hljs-attr">todo</span>: Todo_todo
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span><<span class="hljs-title">Props</span>> </span>{
render() {
<span class="hljs-keyword">const</span> {complete, text} = <span class="hljs-keyword">this</span>.props.todo;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">li</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">input</span>
<span class="hljs-attr">checked</span>=<span class="hljs-string">{complete}</span>
<span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
/></span>
<span class="hljs-tag"><<span class="hljs-name">label</span>></span>
{text}
<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"></<span class="hljs-name">li</span>></span></span>
);
}
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createFragmentContainer(
Todo,
graphql<span class="hljs-string">`
# As a convention, we name the fragment as '<ComponentFileName>_<propName>'
fragment Todo_todo on Todo {
complete
text
}
`</span>
)
</code></pre>
<p>The above code highlights one of Relay's most important principles which is colocation of components with their data dependencies. This is beneficial for a few reasons:</p>
<ul>
<li>It becomes obvious at a glance what data is required to render a given component, without having to search which query in our app is fetching the required data.</li>
<li>As a corollary, the component is de-coupled from the query that renders it. We can change the data dependencies for the component without having to update the queries that render them or worrying about breaking other components.</li>
</ul>
<p>Check out our <a href="./thinking-in-relay">Thinking in Relay</a> guide for more details behind Relay's principles.</p>
<p>Before proceeding, don't forget to run the Relay Compiler with <code>yarn relay</code>.</p>
<h2><a class="anchor" aria-hidden="true" id="composing-fragments"></a><a href="#composing-fragments" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Composing Fragments</h2>
<p>Given that <a href="./fragment-containers">Fragment Containers</a> are just React components, we can compose them as such, and even re-use fragment containers within other fragment containers. As an example, let's see how we would define a <code>TodoList</code> component that just renders a list of todo items, and whether all have been completed or not:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// TodoList.js</span>
<span class="hljs-comment">// OPTIONAL: Flow type generated after running `yarn relay`, defining an Object type with shape of the fragment:</span>
<span class="hljs-keyword">import</span> type {TodoList_userTodoData} <span class="hljs-keyword">from</span> <span class="hljs-string">'./__generated__/TodoList_userTodoData.graphql'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {graphql, createFragmentContainer} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>;
type Props = {
<span class="hljs-attr">userTodoData</span>: TodoList_userTodoData,
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span><<span class="hljs-title">Props</span>> </span>{
render() {
<span class="hljs-keyword">const</span> {<span class="hljs-attr">userTodoData</span>: {totalCount, completedCount, todos}} = <span class="hljs-keyword">this</span>.props;
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">section</span>></span>
<span class="hljs-tag"><<span class="hljs-name">input</span>
<span class="hljs-attr">checked</span>=<span class="hljs-string">{totalCount</span> === <span class="hljs-string">completedCount}</span>
<span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
/></span>
<span class="hljs-tag"><<span class="hljs-name">ul</span>></span>
{todos.edges.map(edge =>
<span class="hljs-tag"><<span class="hljs-name">Todo</span>
<span class="hljs-attr">key</span>=<span class="hljs-string">{edge.node.id}</span>
{/*<span class="hljs-attr">We</span> <span class="hljs-attr">pass</span> <span class="hljs-attr">the</span> <span class="hljs-attr">data</span> <span class="hljs-attr">required</span> <span class="hljs-attr">by</span> <span class="hljs-attr">Todo</span> <span class="hljs-attr">here</span>*/}
<span class="hljs-attr">todo</span>=<span class="hljs-string">{edge.node}</span>
/></span>
)}
<span class="hljs-tag"></<span class="hljs-name">ul</span>></span>
<span class="hljs-tag"></<span class="hljs-name">section</span>></span></span>
);
}
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createFragmentContainer(
TodoList,
graphql<span class="hljs-string">`
# As a convention, we name the fragment as '<ComponentFileName>_<PropName>'
fragment TodoList_userTodoData on User {
todos(
first: 2147483647 # max GraphQLInt, to fetch all todos
) {
edges {
node {
id,
# We use the fragment defined by the child Todo component here
...Todo_todo,
},
},
},
id,
totalCount,
completedCount,
}
`</span>,
);
</code></pre>
<p>As with the first fragment container we defined, <code>TodoList</code> declares it's data dependencies via a fragment. However, this component additionally re-uses the fragment previously defined by the <code>Todo</code> component, and passes the appropriate data to when rendering the child <code>Todo</code> components (a.k.a. fragment containers).</p>
<p>One final thing to note when composing fragment containers, is that the parent will not have access to the data defined by the child container, i.e. Relay only allows components to access data they specifically ask for in GraphQL fragments — nothing more. This is called <a href="./thinking-in-relay#data-masking">Data Masking</a>, and it's intentional to prevent components from depending on data they didn't declare as a dependency.</p>
<h2><a class="anchor" aria-hidden="true" id="rendering-fragments"></a><a href="#rendering-fragments" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Rendering Fragments</h2>
<p>Now that we have some components (a.k.a fragment containers) that declare their data dependencies, we need to hook them up to a <code>QueryRenderer</code> so that the data is actually fetched and rendered; remember,
fragment containers do not directly fetch data. Instead, containers declare a specification of the data needed to render, and Relay guarantees that this data is available before rendering.</p>
<p>A <code>QueryRenderer</code> rendering these fragment containers could look like the following:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// ViewerTodoList.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">'prop-types'</span>;
<span class="hljs-keyword">import</span> {graphql, QueryRenderer} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>;
<span class="hljs-keyword">import</span> TodoList <span class="hljs-keyword">from</span> <span class="hljs-string">'./TodoList'</span>
<span class="hljs-keyword">const</span> environment = <span class="hljs-comment">/* defined or imported above... */</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ViewerTodoList extends React.Component {
render() {
<span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">QueryRenderer</span>
<span class="hljs-attr">environment</span>=<span class="hljs-string">{environment}</span>
<span class="hljs-attr">query</span>=<span class="hljs-string">{graphql</span>`
<span class="hljs-attr">query</span> <span class="hljs-attr">ViewerQuery</span> {
<span class="hljs-attr">viewer</span> {
<span class="hljs-attr">id</span>
# <span class="hljs-attr">Re-use</span> <span class="hljs-attr">the</span> <span class="hljs-attr">fragment</span> <span class="hljs-attr">here</span>
<span class="hljs-attr">...TodoList_userTodoData</span>
}
}
`}
<span class="hljs-attr">variables</span>=<span class="hljs-string">{{}}</span>
<span class="hljs-attr">render</span>=<span class="hljs-string">{({error,</span> <span class="hljs-attr">props</span>}) =></span> {
if (error) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Error!<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
if (!props) {
return <span class="hljs-tag"><<span class="hljs-name">div</span>></span>Loading...<span class="hljs-tag"></<span class="hljs-name">div</span>></span>;
}
return (
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span>></span>Todo list for User {props.user.id}:<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">TodoList</span> <span class="hljs-attr">userTodoData</span>=<span class="hljs-string">{props.user.userTodoData}</span> /></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
);
}}
/></span>
);
}
}
</code></pre>
<p>Check out or docs for <a href="./fragment-container">Fragment Containers</a> for more details, and our guides on <a href="./refetch-container">Refetch</a> and <a href="./pagination-container">Pagination</a> for more advanced usage of containers.</p>
<h2><a class="anchor" aria-hidden="true" id="mutating-data"></a><a href="#mutating-data" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Mutating Data</h2>
<p>Now that we know how to query for and render data, let's move on to changing our data. We know that to change any data in our server, we need to use GraphQL <a href="http://graphql.org/learn/queries/#mutations">Mutations</a>.</p>
<p>From our <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql#L35">schema</a>, we know that we have some mutations available to us, so let's start by writing a mutation to change the <code>complete</code> status of a given todo item (i.e. mark or unmark it as done):</p>
<pre><code class="hljs css language-graphql">mutation <span class="hljs-constructor">ChangeTodoStatusMutation($<span class="hljs-params">input</span>: ChangeTodoStatusInput!)</span> {
change<span class="hljs-constructor">TodoStatus(<span class="hljs-params">input</span>: $<span class="hljs-params">input</span>)</span> {
todo {
id
complete
}
}
}
</code></pre>
<p>This mutation allows us to query back some data as a <a href="https://github.com/relayjs/relay-examples/blob/master/todo/data/schema.graphql#L18">result of the mutation</a>, so we're going to query for the updated <code>complete</code> status on the todo item.</p>
<p>In order to execute this mutation in Relay, we're going to write a new mutation using Relay's <code>commitMutation</code> api:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// ChangeTodoStatusMutation.js</span>
<span class="hljs-keyword">import</span> {graphql, commitMutation} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-relay'</span>;
<span class="hljs-comment">// We start by defining our mutation from above using `graphql`</span>
<span class="hljs-keyword">const</span> mutation = graphql<span class="hljs-string">`
mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) {
changeTodoStatus(input: $input) {
todo {
id
complete
}
}
}
`</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">commit</span>(<span class="hljs-params">
environment,
complete,
todo,
</span>) </span>{
<span class="hljs-comment">// Now we just call commitMutation with the appropriate parameters</span>
<span class="hljs-keyword">return</span> commitMutation(
environment,
{
mutation,
<span class="hljs-attr">variables</span>: {
<span class="hljs-attr">input</span>: {complete, <span class="hljs-attr">id</span>: todo.id},
},
}
);
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {commit};
</code></pre>
<p>Whenever we call <code>ChangeTodoStatusMutation.commit(...)</code>, Relay will send the mutation to the server, and in our case, upon receiving a response it will automatically update the local data store with the latest data from the server. This also means that upon receiving the response, Relay will ensure that any components (i.e. containers) that depend on the updated data are re-rendered.</p>
<p>In order to actually use this mutation in our component, we could update our <code>Todo</code> component in the following way:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// Todo.js</span>
<span class="hljs-comment">// ...</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span><<span class="hljs-title">Props</span>> </span>{
<span class="hljs-comment">// Add a new event handler that fires off the mutation</span>
_handleOnCheckboxChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {
<span class="hljs-keyword">const</span> complete = e.target.checked;
ChangeTodoStatusMutation.commit(
<span class="hljs-keyword">this</span>.props.relay.environment,
complete,
<span class="hljs-keyword">this</span>.props.todo,
);
};
render() {
<span class="hljs-comment">// ...</span>
}
}
<span class="hljs-comment">// ...</span>
</code></pre>
<h3><a class="anchor" aria-hidden="true" id="optimistic-updates"></a><a href="#optimistic-updates" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Optimistic Updates</h3>
<p>In our example above, the <code>complete</code> status in our component won't be updated and re-rendered until we get a response back from the server, which won't make for a great user experience.</p>
<p>In order to make the experience better, we can configure our mutation to do an optimistic update. An optimistic update means immediately updating our local data with what we expect it to be if we get a successful response from the server, i.e. updating the data immediately assuming that the mutation request will succeed. If the request doesn't succeed, we can roll-back our update.</p>
<p>In Relay, there's a couple of options we can pass to <code>commitMutation</code> to enable optimistic updates. Let's see what that would look like in our <code>ChangeTodoStatusMutation</code>:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-comment">// ChangeTodoStatusMutation.js</span>
<span class="hljs-comment">// ...</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getOptimisticResponse</span>(<span class="hljs-params">complete, todo</span>) </span>{
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">changeTodoStatus</span>: {
<span class="hljs-attr">todo</span>: {
<span class="hljs-attr">complete</span>: complete,
<span class="hljs-attr">id</span>: todo.id,
},
},
};
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">commit</span>(<span class="hljs-params">
environment,
complete,
todo
</span>) </span>{
<span class="hljs-comment">// Now we just call commitMutation with the appropriate parameters</span>
<span class="hljs-keyword">return</span> commitMutation(
environment,
{
mutation,
<span class="hljs-attr">variables</span>: {
<span class="hljs-attr">input</span>: {complete, <span class="hljs-attr">id</span>: todo.id},
},
<span class="hljs-attr">optimisticResponse</span>: getOptimisticResponse(complete, todo),
}
);
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {commit};
</code></pre>
<p>In the simplest case above, we just need to pass an <code>optimisticResponse</code> option, which should refer to an object having the same shape as the mutation response payload. When we pass this option, Relay will know to immediately update our local data with the optimistic response, and then update it with the actual server response, or roll it back if an error occurs.</p>
<h3><a class="anchor" aria-hidden="true" id="updating-local-data-from-mutation-responses"></a><a href="#updating-local-data-from-mutation-responses" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Updating local data from mutation responses</h3>
<p>By default, Relay will know to update the fields on the records referenced by the mutation payload, (i.e. the <code>todo</code> in our example). However, this is only the simplest case, and in some cases updating the local data isn't as simple as just updating the fields in a record.</p>
<p>For instance, we might be updating a collection of items, or we might be deleting a record entirely. For these more advanced scenarios, Relay allows us to pass a set of options for us to control how we update the local data from a server response, including a set of <a href="./mutations.html#configs"><code>configs</code></a>, and an <a href="https://facebook.github.io/relay/docs/en/mutations.html#updating-the-store-programatically-advanced"><code>updater</code></a> function for full control over the update.</p>
<p>For more details and advanced use cases on mutations and updates, check out our <a href="./mutations.html">Mutations</a> docs.</p>
<h2><a class="anchor" aria-hidden="true" id="next-steps"></a><a href="#next-steps" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Next Steps</h2>
<p>This guide just scratches the surface of Relay's API; for more detailed docs and guides, check out our API Reference and Guides sections.</p>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 5/15/2020 by Tim Yung</em></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#setup">Setup</a></li><li><a href="#relay-environment">Relay Environment</a></li><li><a href="#rendering-graphql-queries">Rendering GraphQL Queries</a></li><li><a href="#using-query-variables">Using Query Variables</a></li><li><a href="#using-fragments">Using Fragments</a></li><li><a href="#composing-fragments">Composing Fragments</a></li><li><a href="#rendering-fragments">Rendering Fragments</a></li><li><a href="#mutating-data">Mutating Data</a><ul class="toc-headings"><li><a href="#optimistic-updates">Optimistic Updates</a></li><li><a href="#updating-local-data-from-mutation-responses">Updating local data from mutation responses</a></li></ul></li><li><a href="#next-steps">Next Steps</a></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><a href="/" class="nav-home"><img src="/img/relay.svg" alt="Relay" width="66" height="58"/></a><div><h5>Docs</h5><a href="/docs/en/introduction-to-relay.html">Introduction</a></div><div><h5>Community</h5><a href="/en/users.html">User Showcase</a></div><div><h5>More</h5><a href="https://github.com/facebook/relay">GitHub</a><a class="github-button" href="https://github.com/facebook/relay" data-icon="octicon-star" data-count-href="/facebook/relay/stargazers" data-count-api="/repos/facebook/relay#stargazers_count" data-count-aria-label="# stargazers on GitHub" aria-label="Star this project on GitHub">Star</a></div></section><a href="https://code.facebook.com/projects/" target="_blank" class="fbOpenSource"><img src="/img/oss_logo.png" alt="Facebook Open Source" width="170" height="45"/></a><section class="copyright">Copyright © 2020 Facebook Inc.</section></footer></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script><script>
document.addEventListener('keyup', function(e) {
if (e.target !== document.body) {
return;
}
// keyCode for '/' (slash)
if (e.keyCode === 191) {
const search = document.getElementById('search_input_react');
search && search.focus();
}
});
</script><script>
var search = docsearch({
apiKey: '3d7d5825d50ea36bca0e6ad06c926f06',
indexName: 'relay',
inputSelector: '#search_input_react',
algoliaOptions: {"facetFilters":["version:classic"]}
});
</script></body></html>