how to not fetch fragment data until component renders react GraphQL

342 views Asked by At

I thought that relay modern implemented a system whereby it would not try to fetch data until it was rendering the component that declared it. I am talking about fragment components. I have tried to test this but it is fetching all the data.

import React from "react";
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import {
  RelayEnvironmentProvider,
} from "react-relay/hooks";
import "./App.css";
import QueryLoaderComponent from "./QueryLoaderComponent";
import QueryComponent from "./QueryComponent";

async function fetchGraphQL(text: string, variables: Record<any, any>) {

  // Fetch data from GitHub's GraphQL API:
  const response = await fetch("https://countries.trevorblades.com/", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: text,
      variables,
    }),
  });

  // Get the response as JSON
  return await response.json();
}

async function fetchRelay(params: any, variables: any) {
  console.log(
    `fetching query ${params.name} with ${JSON.stringify(variables)}`
  );
  return fetchGraphQL(params.text, variables);
}

// Export a singleton instance of Relay Environment configured with our network function:
const environment = new Environment({
  network: Network.create(fetchRelay),
  store: new Store(new RecordSource()),
});

function App() {
  
  return (
    <RelayEnvironmentProvider environment={environment}>
      {/* <QueryLoaderComponent /> */}
      <QueryComponent />
    </RelayEnvironmentProvider>
  );
}

export default App;
import { useState } from "react";
// @ts-ignore
import graphql from "babel-plugin-relay/macro";
import { QueryComponentQuery } from "./__generated__/QueryComponentQuery.graphql";
import { PreloadedQuery, useLazyLoadQuery, usePreloadedQuery } from "react-relay";
// import FragmentComponent from "./FragmentComponent";

const query = graphql`
  query QueryComponentQuery($id: ID!) {
    country(code: $id) {
      name
      ...FragmentComponent_country
    }
  }
`;

interface Props {
  // queryRef: PreloadedQuery<QueryComponentQuery>;
}

const QueryComponent = ({
  // queryRef
}: Props) => {
  const data = useLazyLoadQuery<QueryComponentQuery>(query, { id: "US"});
  const [showContinent, setShowContinent] = useState(false);
  return (
    <div>
      <button onClick={() => setShowContinent(!showContinent)}>
        {showContinent ? "Hide" : "Show"} continent
      </button>
      <h1>{data.country?.name}</h1>
      {/* <ul>
        {data.countries.map((country: any) => (
          <li key={country.name}>
            {country.name}{" "}
            {showContinent && <FragmentComponent country={country} />}
          </li>
        ))}
      </ul> */}
    </div>
  );
};

export default QueryComponent;
import { useFragment } from "react-relay";
// @ts-ignore
import graphql from "babel-plugin-relay/macro";
import { FragmentComponent_country$key } from "./__generated__/FragmentComponent_country.graphql";

export const fragment = graphql`
  fragment FragmentComponent_country on Country {
    continent {
      name
    }
  }
`;

interface Props {
  country: FragmentComponent_country$key;
}

const FragmentComponent = ({ country }: Props) => {
  const data = useFragment(fragment, country);
  return <div>{data.continent.name}</div>;
};

export default FragmentComponent;

this is fetching the data for the fragment component even though it is not rendering the fragment component. is there a way to defer it until it is rendering the component?

2

There are 2 answers

0
gpbaculio On

use

React Suspense

on the fragment or anywhere where fetching happens as wrapper

0
FINDarkside On

I think there's some kind of misunderstanding, since one of the biggest benefits of Relay is that everything can be loaded in one query and that relay can start loading all of that data before everything has been rendered. However if you want your component to fetch data only when it's rendered, you can use useLazyLoadQuery

You don't need fragments, just pass your GraphQL query to the hook and wrap the component in Suspense and you're good to go.