PageSourceSearch

https://relay.dev/assets/js/6c286649.496601e7.js

js relay.dev collected 2026-10-02 03:13:40 UTC 19,003 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["19806"],{28630(e,n,t){t.r(n),t.d(n,{metadata:()=>r,default:()=>l,frontMatter:()=>s,contentTitle:()=>o,toc:()=>d,assets:()=>c});var r=JSON.parse('{"id":"guided-tour/refetching/OssAvoidSuspenseNote","title":"OssAvoidSuspenseNote","description":"In future versions of React when concurrent rendering is supported, React will provide an option to support this case and avoid hiding already rendered content with a Suspense fallback when suspending.","source":"@site/versioned_docs/version-v14.0.0/guided-tour/refetching/OssAvoidSuspenseNote.md","sourceDirName":"guided-tour/refetching","slug":"/guided-tour/refetching/OssAvoidSuspenseNote","permalink":"/docs/v14.0.0/guided-tour/refetching/OssAvoidSuspenseNote","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/relay/tree/main/website/versioned_docs/version-v14.0.0/guided-tour/refetching/OssAvoidSuspenseNote.md","tags":[],"version":"v14.0.0","frontMatter":{}}'),a=t(74848),i=t(28453);let s={},o,c={},d=[];function h(e){let n={admonition:"admonition",p:"p",...(0,i.R)(),...e.components};return(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"In future versions of React when concurrent rendering is supported, React will provide an option to support this case and avoid hiding already rendered content with a Suspense fallback when suspending."})})}function l(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},86332(e,n,t){t.r(n),t.d(n,{metadata:()=>r,default:()=>m,frontMatter:()=>d,contentTitle:()=>h,toc:()=>f,assets:()=>l});var r=JSON.parse('{"id":"guided-tour/refetching/refetching-fragments-with-different-data","title":"Refetching Fragments with Different Data","description":"Relay guide to refetching fragments with different data","source":"@site/versioned_docs/version-v14.0.0/guided-tour/refetching/refetching-fragments-with-different-data.md","sourceDirName":"guided-tour/refetching","slug":"/guided-tour/refetching/refetching-fragments-with-different-data/","permalink":"/docs/v14.0.0/guided-tour/refetching/refetching-fragments-with-different-data/","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/relay/tree/main/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refetching-fragments-with-different-data.md","tags":[],"version":"v14.0.0","frontMatter":{"id":"refetching-fragments-with-different-data","title":"Refetching Fragments with Different Data","slug":"/guided-tour/refetching/refetching-fragments-with-different-data/","description":"Relay guide to refetching fragments with different data","keywords":["refetching","fragment"]}}'),a=t(74848),i=t(28453),s=t(86898),o=t(17352),c=t(28630);let d={id:"refetching-fragments-with-different-data",title:"Refetching Fragments with Different Data",slug:"/guided-tour/refetching/refetching-fragments-with-different-data/",description:"Relay guide to refetching fragments with different data",keywords:["refetching","fragment"]},h,l={},f=[{value:"Using <code>useRefetchableFragment</code>",id:"using-userefetchablefragment",level:2},{value:"If you need to avoid Suspense",id:"if-you-need-to-avoid-suspense",level:3},...c.toc];function u(e){let n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["When referring to ",(0,a.jsx)(n.strong,{children:'"refetching a fragment"'}),", we mean fetching a ",(0,a.jsx)(n.em,{children:"different"})," version of the data than the one was originally rendered by the fragment. For example, this might be to change a currently selected item, to render a different list of items than the one being shown, or more generally to transition the currently rendered content to show new or different content."]}),"\n",(0,a.jsxs)(n.p,{children:["Conceptually, this means fetching and rendering the currently rendered fragment again, but under a new query with ",(0,a.jsx)(n.em,{children:"different variables"}),"; or in other words, rendering the fragment under a new query root. Remember that ",(0,a.jsx)(n.em,{children:"fragments can't be fetched by themselves: they need to be part of a query,"}),' so we can\'t just "fetch" the fragment again by itself.']}),"\n",(0,a.jsxs)(n.h2,{id:"using-userefetchablefragment",children:["Using ",(0,a.jsx)(n.code,{children:"useRefetchableFragment"})]}),"\n",(0,a.jsxs)(n.p,{children:["To do so, we can also use the ",(0,a.jsx)(n.a,{href:"../../../api-reference/use-refetchable-fragment/",children:(0,a.jsx)(n.code,{children:"useRefetchableFragment"})})," Hook in combination with the ",(0,a.jsx)(n.code,{children:"@refetchable"})," directive, which will automatically generate a query to refetch the fragment under, and which we can fetch using the ",(0,a.jsx)(n.code,{children:"refetch"})," function:"]}),"\n",(0,a.jsxs)(o.OssOnly,{children:[(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-js",children:"import type {CommentBodyRefetchQuery} from 'CommentBodyRefetchQuery.graphql';\nimport type {CommentBody_comment$key} from 'CommentBody_comment.graphql';
1\n\ntype Props = {\n  comment: CommentBody_comment$key,\n};\n\nfunction CommentBody(props: Props) {\n  const [data, refetch] = useRefetchableFragment<CommentBodyRefetchQuery, _>(\n    graphql`\n      fragment CommentBody_comment on Comment\n      # @refetchable makes it so Relay autogenerates a query for\n      # fetching this fragment\n      @refetchable(queryName: \"CommentBodyRefetchQuery\") {\n        body(lang: $lang) {\n          text\n        }\n      }\n    `,\n    props.comment,\n  );\n\n  const refetchTranslation = () => {\n    // We call refetch with new variables,\n    // which will refetch the @refetchable query with the\n    // new variables and update this component with the\n    // latest fetched data.\n    refetch({lang: 'SPANISH'});\n  };\n\n  return (\n    <>\n      <p>{data.body?.text}</p>\n      <Button\n        onClick={() => refetchTranslation()}>\n        Translate Comment\n      </Button>\n    </>\n  );\n}\n"})}),(0,a.jsx)(n.p,{children:"Let's distill what's happening in this example:"}),(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"useRefetchableFragment"})," behaves similarly to ",(0,a.jsx)(n.a,{href:"../../../api-reference/use-fragment/",children:(0,a.jsx)(n.code,{children:"useFragment"})})," (see the ",(0,a.jsx)(n.a,{href:"../../rendering/fragments/",children:"Fragments"})," section), but with a few additions:","\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["It expects a fragment that is annotated with the ",(0,a.jsx)(n.code,{children:"@refetchable"})," directive. Note that ",(0,a.jsx)(n.code,{children:"@refetchable"}),' directive can only be added to fragments that are "refetchable", that is, on fragments that are on ',(0,a.jsx)(n.code,{children:"Viewer"}),", on ",(0,a.jsx)(n.code,{children:"Query"}),", on any type that implements ",(0,a.jsx)(n.code,{children:"Node"})," (i.e. a type that has an ",(0,a.jsx)(n.code,{children:"id"})," field), or on a ",(0,a.jsx)(n.a,{href:"https://fb.workplace.com/groups/graphql.fyi/permalink/1539541276187011/",children:(0,a.jsx)(n.code,{children:"@fetchable"})})," type."]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["It returns a ",(0,a.jsx)(n.code,{children:"refetch"})," function, which is already Flow-typed to expect the query variables that the generated query expects."]}),"\n",(0,a.jsxs)(n.li,{children:["It takes two Flow type parameters: the type of the generated query (in our case  ",(0,a.jsx)(n.code,{children:"CommentBodyRefetchQuery"}),"), and a second type which can always be inferred, so you only need to pass underscore (",(0,a.jsx)(n.code,{children:"_"}),")."]}),"\n",(0,a.jsxs)(n.li,{children:["We're calling the ",(0,a.jsx)(n.code,{children:"refetch"})," function with 2 main inputs:","\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["The first argument is the set of variables to fetch the fragment with. In this case, calling ",(0,a.jsx)(n.code,{children:"refetch"})," and passing a new set of variables will fetch the fragment again ",(0,a.jsx)(n.em,{children:"with the newly provided variables"}),". The variables you need to provide are a subset of the variables that the ",(0,a.jsx)(n.code,{children:"@refetchable"})," query expects; the query will require an ",(0,a.jsx)(n.code,{children:"id"}),", if the type of the fragment has an ",(0,a.jsx)(n.code,{children:"id"})," field, and any other variables that are transitively referenced in your fragment.","\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["In this case we're passing the current comment ",(0,a.jsx)(n.code,{children:"id"})," and a new value for the ",(0,a.jsx)(n.code,{children:"translationType"})," variable to fetch the translated comment body."]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["We are not passing a second options argument in this case, which means that we will use the default ",(0,a.jsx)(n.code,{children:"fetchPolicy"})," of ",(0,a.jsx)(n.code,{children:"'store-or-network'"}),", which will skip the network request if the new data for that fragment is already cached (as we covered in ",(0,a.jsx)(n.a,{href:"../../reusing-cached-data/",children:"Reusing Cached Data For Render"}),")."]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["Calling ",(0,a.jsx)(n.code,{children:"refetch"})," will re-render the component and may cause ",(0,a.jsx)(n.code,{children:"useRefetchableFragment"})," to suspend (as explained in ",(0,a.jsx)(n.a,{href:"../../rendering/loading-states/",children:"Loading States with Suspense"}),"). This means that you'll need to make sure that there's a ",(0,a.jsx)(n.code,{children:"Suspense"})," boundary wrapping this component from above in order to show a fallback loading state."]}),"\n"]})]}),"\n",(0,a.jsx)(n.admonition,{type:"info",children:(0,a.jsxs)(n.p,{children:["Note that this same behavior also applies to using the ",(0,a.jsx)(n.code,{children:"refetch"})," function from ",(0,a.jsx)(n.a,{href:"../../../api-reference/use-pagination-fragment",children:(0,a.jsx)(n.code,{children:"usePaginationFragment"})}),"."]})}),"\n",(0,a.jsx)(n.h3,{id:"if-you-need-to-avoid-suspense",children:"If you need to avoid Suspense"}),"\n",(0,a.jsxs)(n.p,{children:["In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use ",(0,a.jsx)(n.a,{href:"../../../api-reference/fetch-query/",children:(0,a.jsx)(n.code,{children:"fetchQuery"})})," instead, and manually keep track of a loading state:"]}),"\n",(0,a.jsx)(o.OssOnly,{children:(0,a.jsx)(c.default,{})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-js",children:"import type {CommentBodyRefetchQuery} from 'CommentBodyRefetchQuery.graphql';\nimport type {CommentBody_comment$key} from 'CommentBody_comment.graphql';
1\n\ntype Props = {\n  comment: CommentBody_comment$key,\n};\n\nfunction CommentBody(props: Props) {\n  const [data, refetch] = useRefetchableFragment<CommentBodyRefetchQuery, _>(\n    graphql`\n      fragment CommentBody_comment on Comment\n      # @refetchable makes it so Relay autogenerates a query for\n      # fetching this fragment\n      @refetchable(queryName: \"CommentBodyRefetchQuery\") {\n        body(lang: $lang) {\n          text\n        }\n      }\n    `,\n    props.comment,\n  );\n\n  const [isRefetching, setIsRefreshing] = useState(false)\n  const refetchTranslation = () => {\n    if (isRefetching) { return; }\n    setIsRefreshing(true);\n\n    // fetchQuery will fetch the query and write\n    // the data to the Relay store. This will ensure\n    // that when we re-render, the data is already\n    // cached and we don't suspend\n    fetchQuery(environment, AppQuery, variables)\n      .subscribe({\n        complete: () => {\n          setIsRefreshing(false);\n\n          // *After* the query has been fetched, we call\n          // refetch again to re-render with the updated data.\n          // At this point the data for the query should\n          // be cached, so we use the 'store-only'\n          // fetchPolicy to avoid suspending.\n          refetch({lang: 'SPANISH'}, {fetchPolicy: 'store-only'});\n        }\n        error: () => {\n          setIsRefreshing(false);\n        }\n      });\n  };\n\n  return (\n    <>\n      <p>{data.body?.text}</p>\n      <Button\n        disabled={isRefetching}\n        onClick={() => refetchTranslation()}>\n        Translate Comment {isRefetching ? <LoadingSpinner /> : null}\n      </Button>\n    </>\n  );\n}\n"})}),"\n",(0,a.jsx)(n.p,{children:"Let's distill what's going on here:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["When refetching, we now keep track of our own ",(0,a.jsx)(n.code,{children:"isRefetching"})," loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI in our component, ",(0,a.jsx)(n.em,{children:"without"})," hiding the content."]}),"\n",(0,a.jsxs)(n.li,{children:["In the event handler, we first call ",(0,a.jsx)(n.code,{children:"fetchQuery"}),", which will fetch the query and write the data to the local Relay store. When the ",(0,a.jsx)(n.code,{children:"fetchQuery"})," network request completes, we call ",(0,a.jsx)(n.code,{children:"refetch"})," so that we render the updated data, similar to the previous example."]}),"\n",(0,a.jsxs)(n.li,{children:["At this point, when ",(0,a.jsx)(n.code,{children:"refetch"})," is called, the data for the fragment should already be cached in the local Relay store, so we use ",(0,a.jsx)(n.code,{children:"fetchPolicy"})," of ",(0,a.jsx)(n.code,{children:"'store-only'"})," to avoid suspending and only read the already cached data."]}),"\n"]}),"\n",(0,a.jsx)(s.A,{})]})}function m(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(u,{...e})}):u(e)}},86898(e,n,t){t.d(n,{A:()=>m});var r=t(74848),a=t(95310),i=t(17352),s=t(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_support**",tag_ids:{add:[0xac96423e5b680,0x64079768ac750]}}));window.open("https://www.internalfb.com/tasks/?n="+e)}function c({children:e}){return(0,r.jsxs)("div",{className:"docsRating",id:"docsRating",children:[(0,r.jsx)("hr",{}),e]})}let d=()=>{let[e,n]=s.useState(!1),t=e=>{n(!0),window.ga&&window.ga("send",{hitType:"event",eventCategory:"button",eventAction:"feedback",eventValue:e})};return e?"Thank you for letting us know!":(0,r.jsxs)(r.Fragment,{children:["Is this page useful?",(0,r.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:()=>t(1),children:(0,r.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,r.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:()=>t(0),children:(0,r.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"})})]})},h=()=>(0,r.jsxs)("p",{children:["Let us know how these docs can be improved by",(0,r.jsx)("a",{className:"button",role:"button",tabIndex:0,onClick:o,children:"Filing a task"})]}),l=()=>(0,r.jsxs)("p",{children:["Help us make the site even better by"," ",(0,r.jsx)(a.default,{to:"https://www.surveymonkey.com/r/FYC9TCJ",children:"answering a few quick questions"}),"."]}),f=()=>(0,r.jsxs)(c,{children:[(0,r.jsx)(h,{}),(0,r.jsx)(d,{}),(0,r.jsx)(l,{})]}),u=()=>(0,r.jsxs)(c,{children:[(0,r.jsx)(d,{}),(0,r.jsx)(l,{})]}),m=()=>(0,i.fbContent)({internal:(0,r.jsx)(f,{}),external:(0,r.jsx)(u,{})})},28453(e,n,t){t.d(n,{R:()=>s,x:()=>o});var r=t(96540);let a={},i=r.createContext(a);function s(e){let n=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),r.createElement(i.Provider,{value:n},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.