Declarative rendering with Apollo Client results
Mike Schutte
Posted on December 7, 2020
RedwoodJS introduces a great abstraction for dealing with query results from Apollo Client: cells.
If you've used Apollo Client before, you've probably written something like the following hundreds of times.
const { loading, data } = useQuery(...)
if (loading) {
return ...
}
if (data.length === 0) {
return ...
}
return (
...
)
Am I wrong?
I love the idea of cells. I can tell it's a great abstraction because there's no need to port my whole app over to RedwoodJS to get the same immediate declarative improvements. Here is a Redwoods-y utility function to render the result of a GraphQL query in any codebase with Apollo Client query results.
import * as React from "react";
import { ApolloError, QueryResult } from "@apollo/client";
const isEmpty = (data: NonNullable<QueryResult["data"]>): boolean => {
const dataValue = data[Object.keys(data)[0]];
return (
dataValue === null || (Array.isArray(dataValue) && dataValue.length === 0)
);
};
export const renderResult = <T extends QueryResult>(
result: T,
options: {
isEmpty?: (data: NonNullable<T["data"]>) => boolean;
Loading: React.FC;
Failure: React.FC<{ error: ApolloError }>;
Empty: React.FC;
Success: React.FC<{ data: NonNullable<T["data"]> }>;
}
): React.ReactElement => {
return result.loading ? (
<options.Loading />
) : result.error ? (
<options.Failure error={result.error} />
) : (options.isEmpty ?? isEmpty)(result.data) ? (
<options.Empty />
) : (
<options.Success data={result.data} />
);
};
We can pass a custom isEmpty
function if the shape of our data is more unique than the basic case.
Example usage:
import * as React from "react";
import { render } from "lib/render-result";
const MyComponent: React.FC = () => {
const result = useQuery(...)
return render(result, {
Loading,
Failure,
Success,
Empty,
isEmpty: (data) => data.customPath.toCheck.length === 0
});
};
export default MyComponent;
const Loading: React.FC = () => {
return ...
};
const Empty: React.FC = () => {
return ...
};
type Data = NonNullable<QueryResult["data"]>;
const Success: React.FC<{ data: Data }> = ({ data }) => {
return ...
};
type FetchError = NonNullable<QueryResult["error"]>;
const Failure: React.FC<{ error: FetchError }> = ({ error }) => {
console.error(error);
return ...
};
✌️
💖 💪 🙅 🚩
Mike Schutte
Posted on December 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.