generated-data-loaders
Here's an improved version of your document:
Generated Data Loaders
To minimize repetitive work, we automatically generate Remix data loaders by analyzing the page source code. Our parser scans the page for GraphQL queries and calls all of those queries in the generated data loaders. This ensures that by the time the component requests the data, it is already available in the Apollo Cache, reducing redundancy.
The following example demonstrates how a component's code is used to generate data loaders:
#Teams.tsx
const Teams = (props) => {
const [data, { loading }] = useTeamsQuery({ variables: { orgName, pageSize: 10 } });
// rest of the business logic
return (
// component render Markup
)
}
When our Rollup plugin parses the component and detects the GraphQL queries, it creates a key-value pair where the key represents the GraphQL document the query uses, and the value refers to the variables used by the query. Note the following:
- The GraphQL document must be present in the
core
package. For example, if a component is insideaccount-browser
, the GraphQL document should be exported from either theclient
orcore
package. - Variables passed in the
variables
parameter will be converted toparams.<variableName>
, and the loader will expect these values in the params.
For the above component, the following routes.json
structure will be created:
{
"queries": {
"TeamsDocument": "{orgName: params.orgName, pageSize: 10}"
}
}
This serves as meta-information for our generator function, which uses it to create loaders for both server and client. The generated loaders will look something like this:
export const loaders = ({ params, context }) => {
const { apolloClient: client } = context;
const queries = { GetTeamsDocument: { orgName: params.orgName, pageSize: 10 } };
const organizationTeamsQuery = client.query({
query: GetTeamsDocument,
variables: queries['GetTeamsDocument'],
fetchPolicy: __SERVER__ ? 'network-only' : 'cache-first',
});
return {
data: {
organizationTeamsQuery
}
};
}
const clientLoader = async ({ params, serverLoader }) => {
const client = window.__APOLLO_CLIENT__;
try {
const getKey = (documentName) => camelCase(documentName).replace('Document', '');
const queries = { GetTeamsDocument: { orgName: params.orgName, pageSize: 10 } };
const queryKeys = ['TeamsQuery'];
let shouldCallServerLoader = false;
let response = {};
let cachedData, cacheKey;
cachedData = client.cache.readQuery({
query: GetTeamsDocument,
variables: queries['GetTeamsDocument'],
});
cacheKey = getKey('GetTeamsDocument');
if (!cachedData) {
shouldCallServerLoader = true;
}
if (cachedData && cachedData[cacheKey]) {
response[queryKeys[0]] = Promise.resolve(cachedData[cacheKey]);
}
if (!shouldCallServerLoader) return response;
const serverData = await serverLoader();
let queryKey = queryKeys[0];
if (!serverData[queryKey].then) {
return;
}
serverData[queryKey].then(({ data }) => {
client.cache.writeQuery({
query: GetTeamsDocument,
variables: queries['GetTeamsDocument'],
data,
});
});
return serverData;
} catch (err) {
console.error('Error in clientLoader', err);
}
}
The above document provides an example for a simple use case where a component has only one query. For components with multiple queries, the generated loaders will be longer.