1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["10176"],{2745(e,t,a){a.r(t),a.d(t,{metadata:()=>n,default:()=>p,frontMatter:()=>o,contentTitle:()=>c,toc:()=>l,assets:()=>h});var n=JSON.parse('{"id":"principles-and-architecture/thinking-in-relay","title":"Thinking in Relay","description":"Relay guide to thinking in Relay","source":"@site/versioned_docs/version-v14.0.0/principles-and-architecture/thinking-in-relay.md","sourceDirName":"principles-and-architecture","slug":"/principles-and-architecture/thinking-in-relay/","permalink":"/docs/v14.0.0/principles-and-architecture/thinking-in-relay/","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/relay/tree/main/website/versioned_docs/version-v14.0.0/principles-and-architecture/thinking-in-relay.md","tags":[],"version":"v14.0.0","frontMatter":{"id":"thinking-in-relay","title":"Thinking in Relay","slug":"/principles-and-architecture/thinking-in-relay/","description":"Relay guide to thinking in Relay"},"sidebar":"docs","previous":{"title":"Thinking in GraphQL","permalink":"/docs/v14.0.0/principles-and-architecture/thinking-in-graphql/"},"next":{"title":"Architecture Overview","permalink":"/docs/v14.0.0/principles-and-architecture/architecture-overview/"}}'),i=a(74848),r=a(28453),s=a(86898);let o={id:"thinking-in-relay",title:"Thinking in Relay",slug:"/principles-and-architecture/thinking-in-relay/",description:"Relay guide to thinking in Relay"},c,h={},l=[{value:"Fetching Data For a View",id:"fetching-data-for-a-view",level:2},{value:"Specifying the data requirements of a component",id:"specifying-the-data-requirements-of-a-component",level:2},{value:"Queries",id:"queries",level:2},{value:"Data Masking",id:"data-masking",level:2}];function d(e){let t={code:"code",em:"em",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(t.p,{children:["Relay's approach to data-fetching is heavily inspired by our experience with React. In particular, React breaks complex interfaces into reusable ",(0,i.jsx)(t.strong,{children:"components"}),", allowing developers to reason about discrete units of an application in isolation, and reducing the coupling between disparate parts of an application. Even more important is that these components are ",(0,i.jsx)(t.strong,{children:"declarative"}),": they allow developers to specify ",(0,i.jsx)(t.em,{children:"what"})," the UI should look like for a given state, and not have to worry about ",(0,i.jsx)(t.em,{children:"how"})," to show that UI. Unlike previous approaches that used imperative commands to manipulate native views (e.g. the DOM), React uses a UI description to automatically determine the necessary commands."]}),"\n",(0,i.jsx)(t.p,{children:"Let's look at some product use-cases to understand how we incorporated these ideas into Relay. We'll assume a basic familiarity with React."}),"\n",(0,i.jsx)(t.h2,{id:"fetching-data-for-a-view",children:"Fetching Data For a View"}),"\n",(0,i.jsx)(t.p,{children:"In our experience, the overwhelming majority of products want one specific behavior: fetch all the data for a view hierarchy while displaying a loading indicator, and then render the entire view once the data is available."}),"\n",(0,i.jsx)(t.p,{children:"One solution is to have a root component declare and fetch the data required by it and all of its children. However, this would introduce coupling: any change to a child component would require changing any root component that might render it! This coupling could mean a greater chance for bugs and slow the pace of development."}),"\n",(0,i.jsxs)(t.p,{children:["Another logical approach is to have each component declare and fetch the data it requires. This sounds great. However, the problem is that a component may render different children based on the data it received. So, nested components will be unable to render and begin fetching their data until parent components' queries have completed. In other words, ",(0,i.jsx)(t.em,{children:"this forces data fetching to proceed in stages:"})," first render the root and fetch the data it needs, then render its children and fetch their data, and so on until you reach leaf components. Rendering would require multiple slow, serial roundtrips."]}),"\n",(0,i.jsxs)(t.p,{children:["Relay combines the advantages of both of these approaches by allowing components to specify what data they require, but to coalesce those requirements into a single query that fetches the data for an entire subtree of components. In other words, it determines ",(0,i.jsx)(t.em,{children:"statically"})," (i.e. before your application runs;
1 at the time you write your code) the requirements for an entire view!"]}),"\n",(0,i.jsxs)(t.p,{children:["This is achieved with the help of GraphQL. Functional components use one or more GraphQL ",(0,i.jsx)("a",{href:"../../guided-tour/rendering/fragments/",children:"fragments"})," to describe their data requirements. These fragments are then nested within other fragments, and ultimately within queries. And when such a query is fetched, Relay will make a single network request for it and all of its nested fragments. In other words, the Relay runtime is then able to make a ",(0,i.jsx)(t.em,{children:"single network request"})," for all of the data required by a view!"]}),"\n",(0,i.jsx)(t.p,{children:"Let's dive deeper to understand how Relay achieves this feat."}),"\n",(0,i.jsx)(t.h2,{id:"specifying-the-data-requirements-of-a-component",children:"Specifying the data requirements of a component"}),"\n",(0,i.jsxs)(t.p,{children:["With Relay, the data requirements for a component are specified with ",(0,i.jsx)("a",{href:"../../guided-tour/rendering/fragments/",children:"fragments"}),". Fragments are named snippets of GraphQL that specify which fields to select from an object of a particular type. Fragments are written within GraphQL literals. For example, the following declares a GraphQL literal containing a fragment which selects an author's name and photo url:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"// AuthorDetails.react.js\nconst authorDetailsFragment = graphql`\n fragment AuthorDetails_author on Author {\n name\n photo {\n url\n }\n }\n`;\n"})}),"\n",(0,i.jsxs)(t.p,{children:["This data is then read out from the store by calling the ",(0,i.jsx)(t.code,{children:"useFragment(...)"})," hook in a functional React component. The actual author from which to read this data is determined by the second parameter passed to ",(0,i.jsx)(t.code,{children:"useFragment"}),". For example:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"// AuthorDetails.react.js\nexport default function AuthorDetails(props) {\n const data = useFragment(authorDetailsFragment, props.author);\n // ...\n}\n"})}),"\n",(0,i.jsxs)(t.p,{children:["This second parameter (",(0,i.jsx)(t.code,{children:"props.author"}),") is a fragment reference. Fragment references are obtained by ",(0,i.jsx)(t.strong,{children:"spreading"})," a fragment into another fragment or query. Fragments cannot be fetched directly. Instead, all fragments must ultimately be spread (either directly or transitively) into a query for the data to be fetched."]}),"\n",(0,i.jsx)(t.p,{children:"Let's take a look at one such query."}),"\n",(0,i.jsx)(t.h2,{id:"queries",children:"Queries"}),"\n",(0,i.jsxs)(t.p,{children:["In order to fetch that data, we might declare a query which spreads ",(0,i.jsx)(t.code,{children:"AuthorDetails_author"})," as follows:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"// Story.react.js\nconst storyQuery = graphql`\n query StoryQuery($storyID: ID!) {\n story(id: $storyID) {\n title\n author {\n ...AuthorDetails_author\n }\n }\n }\n`;\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Now, we can fetch the query by calling ",(0,i.jsx)(t.code,{children:"const data = useLazyLoadQuery(storyQuery, {storyID})"}),". At this point, ",(0,i.jsx)(t.code,{children:"data.story.author"})," (if it is present; all fields are nullable by default) will be a fragment reference that we can pass to ",(0,i.jsx)(t.code,{children:"AuthorDetails"}),". For example:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-javascript",children:"// Story.react.js\nfunction Story(props) {\n const data = useLazyLoadQuery(storyQuery, props.storyId);\n\n return (<>\n <Heading>{data?.story.title}</Heading>\n {data?.story?.author && <AuthorDetails author={data.story.author} />}\n </>);\n}\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Note what has happened here. We made a single network request which contained the data required by ",(0,i.jsx)(t.em,{children:"both"})," the ",(0,i.jsx)(t.code,{children:"Story"})," component ",(0,i.jsx)(t.em,{children:"and"})," the ",(0,i.jsx)(t.code,{children:"AuthorDetails"})," component! When that data was available, the entire view could render in a single pass."]}),"\n",(0,i.jsx)(t.h2,{id:"data-masking",children:"Data Masking"}),"\n",(0,i.jsxs)(t.p,{children:["With typical approaches to data-fetching we found that it was common for two components to have ",(0,i.jsx)(t.em,{children:"implicit dependencies"}),". For example ",(0,i.jsx)(t.code,{children:"<Story />"})," might use some data without directly ensuring that the data was fetched. This data would often be fetched by some other part of the system, such as ",(0,i.jsx)(t.code,{children:"<AuthorDetails />"}),". Then when we changed ",(0,i.jsx)(t.code,{children:"<AuthorDetails />"})," and removed that data-fetching logic, ",(0,i.jsx)(t.code,{children:"<Story />"})," would suddenly and inexplicably break. These types of bugs are not always immediately apparent, especially in larger applications developed by larger teams. Manual and automated testing can only help so much: this is exactly the type of systematic problem that is better solved by a framework."]}),"\n",(0,i.jsxs)(t.p,{children:["We've seen that Relay ensures that the data for a view is fetched all at once. But Relay also provide another benefit that isn't immediately obvious: ",(0,i.jsx)(t.strong,{children:"data masking"}),". Relay only allows components to access data they specifically ask for in GraphQL fragments, and nothing more. So if one component queries for a Story's ",(0,i.jsx)(t.code,{children:"title"}),", and another for its ",(0,i.jsx)(t.code,{children:"text"}),", each can see ",(0,i.jsx)(t.em,{children:"only"})," the field that they asked for. In fact, components can't even see the data requested by their ",(0,i.jsx)(t.em,{children:"children"}),": that would also break encapsulation."]}),"\n",(0,i.jsxs)(t.p,{children:["Relay also goes further: it uses opaque identifiers on ",(0,i.jsx)(t.code,{children:"props"})," to validate that we've explicitly fetched the data for a component before rendering it. If ",(0,i.jsx)(t.code,{children:"<Story />"})," renders ",(0,i.jsx)(t.code,{children:"<AuthorDetails />"})," but forgets to spread its fragment, Relay will warn that the data for ",(0,i.jsx)(t.code,{children:"<AuthorDetails />"})," is missing. In fact, Relay will warn ",(0,i.jsx)(t.em,{children:"even if"})," some other component happened to fetch the same data required by ",(0,i.jsx)(t.code,{children:"<AuthorDetails />"}),". This warning tells us that although things ",(0,i.jsx)(t.em,{children:"might"})," work now, they're highly likely to break later."]}),"\n",(0,i.jsx)(t.h1,{id:"conclusion",children:"Conclusion"}),"\n",(0,i.jsxs)(t.p,{children:["GraphQL provides a powerful tool for building efficient, decoupled client applications. Relay builds on this functionality to provide a framework for ",(0,i.jsx)(t.strong,{children:"declarative data-fetching"}),". By separating ",(0,i.jsx)(t.em,{children:"what"})," data to fetch from ",(0,i.jsx)(t.em,{children:"how"})," it is fetched, Relay helps developers build applications that are robust, transparent, and performant by default. It's a great complement to the component-centric way of thinking championed by React. While each of these technologies \u2014 React, Relay, and GraphQL \u2014 are powerful on their own, the combination is a ",(0,i.jsx)(t.strong,{children:"UI platform"})," that allows us to ",(0,i.jsx)(t.em,{children:"move fast"})," and ",(0,i.jsx)(t.em,{children:"ship high-quality apps at scale"}),"."]}),"\n",(0,i.jsx)(s.A,{})]})}function p(e={}){let{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},86898(e,t,a){a.d(t,{A:()=>m});var n=a(74848),i=a(95310),r=a(17352),s=a(96540);function o(){let e=window.encodeURI(JSON.stringify({title:"Feedback about "+window.location.pathname,description:"**!!! Required !!!**\n\nPlease modify the task description to let us know how the docs can be improved.\n\n**Please do not ask support questions via this form! Instead, ask in fburl.com/relay_
1support**",tag_ids:{add:[0xac96423e5b680,0x64079768ac750]}}));window.open("https://www.internalfb.com/tasks/?n="+e)}function c({children:e}){return(0,n.jsxs)("div",{className:"docsRating",id:"docsRating",children:[(0,n.jsx)("hr",{}),e]})}let h=()=>{let[e,t]=s.useState(!1),a=e=>{t(!0),window.ga&&window.ga("send",{hitType:"event",eventCategory:"button",eventAction:"feedback",eventValue:e})};return e?"Thank you for letting us know!":(0,n.jsxs)(n.Fragment,{children:["Is this page useful?",(0,n.jsx)("svg",{className:"i_thumbsup",alt:"Like",id:"docsRating-like",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>a(1),children:(0,n.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})}),(0,n.jsx)("svg",{className:"i_thumbsdown",alt:"Dislike",id:"docsRating-dislike",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>a(0),children:(0,n.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})})]})},l=()=>(0,n.jsxs)("p",{children:["Let us know how these docs can be improved by",(0,n.jsx)("a",{className:"button",role:"button",tabIndex:0,onClick:o,children:"Filing a task"})]}),d=()=>(0,n.jsxs)("p",{children:["Help us make the site even better by"," ",(0,n.jsx)(i.default,{to:"https://www.surveymonkey.com/r/FYC9TCJ",children:"answering a few quick questions"}),"."]}),p=()=>(0,n.jsxs)(c,{children:[(0,n.jsx)(l,{}),(0,n.jsx)(h,{}),(0,n.jsx)(d,{})]}),u=()=>(0,n.jsxs)(c,{children:[(0,n.jsx)(h,{}),(0,n.jsx)(d,{})]}),m=()=>(0,r.fbContent)({internal:(0,n.jsx)(p,{}),external:(0,n.jsx)(u,{})})},28453(e,t,a){a.d(t,{R:()=>s,x:()=>o});var n=a(96540);let i={},r=n.createContext(i);function s(e){let t=n.useContext(r);return n.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:s(e.components),n.createElement(r.Provider,{value:t},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.