Skip to main content

Don't write network requests over the sub components.

Don't Do Case​

SubComponent with GraphQL Query​

In this case, the SubComponent handles the GraphQL query directly, which should be avoided:

// SubComponent.js
import { gql, useQuery } from '@apollo/client';

const GET_USERS = gql`
query GetUsers {
users {
id
name
email
}
}
`;

function SubComponent() {
const { data, loading, error } = useQuery(GET_USERS);

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;

return (
<div>
<h1>Users</h1>
<ul>
{data.users.map(user => (
<li key={user.id}>
{user.name} ({user.email})
</li>
))}
</ul>
</div>
);
}

export default SubComponent;

Main Page Importing SubComponent​

The main page simply imports and uses SubComponent, without handling the data fetching properly.

// mainPage.js
import SubComponent from './SubComponent';

export default function MainPage() {
return (
<div>
<SubComponent />
</div>
);
}

Do Case​

Step-by-Step Refactor​

  1. Define the GraphQL Query in the Main Page
// mainPage.js
import { gql } from '@apollo/client';

const GET_USERS = gql`
query GetUsers {
users {
id
name
email
}
}
`;
  1. Set Up the Loader Function
// mainPage.js
import { json, LoaderFunction } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { initializeApollo } from '~/utils/apolloClient'; // Utility to initialize Apollo Client
import SubComponent from './SubComponent';

// instead you can also use graphql Query directly with fetch policy set to cache-only to auto generate loaders as described in `generated-data-loaders`
export let loader: LoaderFunction = async () => {
const client = initializeApollo();
try {
const { data } = await client.query({ query: GET_USERS });
return json(data);
} catch (error) {
console.error('Error fetching users:', error);
throw new Response('Error fetching users', { status: 500 });
}
};
  1. Create the SubComponent as a Pure Component
// SubComponent.js
function SubComponent({ users }) {
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} ({user.email})
</li>
))}
</ul>
</div>
);
}

export default SubComponent;
  1. Use the SubComponent in the Main Page
// mainPage.js
import { useLoaderData } from '@remix-run/react';
import SubComponent from './SubComponent';

export default function MainPage() {
const { users } = useLoaderData();

return (
<div>
<SubComponent users={users} />
</div>
);
}

Summary​

Don't Do Case:

  • Placing GraphQL queries inside subcomponents makes it harder to manage data flow and breaks SSR benefits.
  • Subcomponents are not pure and are tightly coupled with data-fetching logic.

Do Case:

  • GraphQL queries are centralized in the main routed page's loader.
  • Data fetching is handled during SSR, improving performance.
  • Subcomponents are kept pure and receive data via props, making them more reusable and easier to test.