0

Imagine a main page at url path / with components ComponentA, ComponentB and ComponentMain. So maybe something like this:

enter image description here

Instead of ComponentA, ComponentB and ComponentMain each making a single GraphQl query on their own, one could hoist up the state and then make one huge query and pass down the needed data to each component. All fine with that. But imagine a user navigates and the content of ComponentMain changes. There are two different ways of navigating:

  1. User is already on / and clicks some link which changes the content of MainComponent. In that case the data for ComponentA and ComponentB is already available from the user's initial call to /. I only want to load the data for MainComponent, not again the data for ComponentA and ComponentB.
  2. User is not already on the page, but directly uses a link like /someOtherMainContent. In that case, the data for ComponentA and ComponentB isn't loaded yet. All data (ComponentA, ComponentB, ComponentMain) needs to be loaded.

I could code all that logic manually from ground up, but that would be very cumbersome. Is relayjs capable of exactly doing such things? Is that possible with Apollo Client? Is there any pattern for this?

I am using React via NextJs 13 and a GraphQl backend (currently independent from NextJs, coded using ASP.Net Core and HotChocolate GraphQl server).

stefan.at.kotlin
  • 15,347
  • 38
  • 147
  • 270

1 Answers1

1

If ComponentMain is a fragment created with useFragment, change it to useRefetchableFragment that will allow you to refetch changing the variables.

If the fragment (or its selection set) is different different in each deep link, then it becomes more complex.

Something like this might work as explained above:

import React, { useState } from 'react';
import { graphql, useRelayEnvironment, useLazyLoadQuery, useRefetchableFragment, useFragment } from 'react-relay';

const MyComponent = () => {
  const environment = useRelayEnvironment();
  const [selectedItemId, setSelectedItemId] = useState(null);

  const data = useLazyLoadQuery(
    graphql`
      query MyComponentQuery($itemId: ID!) {
        sidePanelData {
          ...SidePanel_data
        }
        mainPanelData: mainPanelDataQuery(itemId: $itemId) {
          ...MainPanel_data
        }
      }
    `,
    { itemId: selectedItemId },
    { fetchPolicy: 'store-or-network' }
  );

  const sidePanelData = useFragment(
    graphql`
      fragment SidePanel_data on SidePanelData {
        # side panel fields
      }
    `,
    data.sidePanelData
  );

  const { data: mainPanelData, refetch: refetchMainPanel } = useRefetchableFragment(
    graphql`
      fragment MainPanel_data on MainPanelData
      @refetchable(queryName: "MainPanelRefetchQuery") {
        # main panel fields
      }
    `,
    data.mainPanelData
  );

  const handleLinkClick = (itemId) => {
    setSelectedItemId(itemId);
    refetchMainPanel({ itemId });
  };

  return (
    <>
      <SidePanel data={sidePanelData} />
      <MainPanel data={mainPanelData} onLinkClick={handleLinkClick} />
    </>
  );
};

const SidePanel = ({ data }) => {
  // Render side panel using data
};

const MainPanel = ({ data, onLinkClick }) => {
  // Render main panel using data
  const handleLinkClick = (itemId) => {
    onLinkClick(itemId);
  };

  return (
    <div>
      {/* Render main panel content */}
      <button onClick={() => handleLinkClick(itemId)}>Click me</button>
    </div>
  );
};
martin
  • 11
  • 2