Robert Balicki and Juan Tejada, software engineers at Facebook, recently released Relay Hooks, a set of new APIs for fetching and managing GraphQL data. Relay Hooks have been battle-tested on the Facebook.com rewrite, and are the recommended way to use Relay at Facebook.
Relay Hooks are a set of React Hooks-based APIs that strives to simplify working with GraphQL data and improve the developer experience. Relay Hooks include APIs for fetching queries, loading data with fragments, pagination, refetching, mutations, and subscriptions. According to Balicki and Tejada, Relay Hooks offer sizeable advantages: fewer lines of code and less indirection than the equivalent container-based solution; more complete Flow and Typescript coverage; automation of error-prone tasks (refetch and pagination queries); fetch policy configuration; and lower experienced latency by fetching data before a component renders.
Speculative fetching seeks to improve user experience by fetching data before it is needed. Resource hints, for instance, let developers declaratively prefetch resources. Balicki and Tejada provide another example of prefetching that leverages Relay Hooks to fetch data when a user hovers on a button. As the delay between hover and click on a button is rarely below 150ms (readers may test their click speed here), and users do not perceive latency below 100ms, fetching on hover may provide a 250ms budget for data to arrive and be displayed for an ideal user experience. The code example is as follows:
const UserQuery = graphql`
query UserLinkQuery($userId: ID!) {
user(id: $userId) {
user_details_blurb
}
}
`;
function UserLink({ userId, userName }) {
const [queryReference, loadQuery] = useQueryLoader(UserQuery);
const [isPopoverVisible, setIsPopoverVisible] = useState(false);
const maybePrefetchUserData = useCallback(() => {
if (!queryReference) {
// calling loadQuery will cause this component to re-render.
// During that re-render, queryReference will be defined.
loadQuery({ userId });
}
}, [queryReference, loadQuery]);
const showPopover = useCallback(() => {
maybePrefetchUserData();
setIsPopoverVisible(true);
}, [maybePrefetchUserData, setIsPopoverVisible]);
return <>
<Button
onMouseOver={maybePrefetchUserData}
onPress={showPopover}
>
{userName}
</Button>
{isPopoverVisible && queryReference && (
<Popover>
<React.Suspense fallback={<Glimmer />}>
<UserPopoverContent queryRef={queryReference} />
</React.Suspense>
</Popover>
)}
</>
}
function UserPopoverContent({queryRef}) {
// The following call will Suspend if the request for the data is still
// in flight:
const data = usePreloadedQuery(UserQuery, queryRef);
// ...
}
In the previous code, maybePrefetchUserData
is run on hover and triggers a network request if the query cannot be fulfilled from the local cache. Balicki and Tejada explained:
When the button is finally pressed, the user will thus see content sooner. By contrast, the [existing] container-based APIs initiate network requests when the component renders.
One developer on Reddit reported the better developer experience for pagination:
I’m so excited to play with this. The simplification of pagination is HUGE [in my opinion]
Relay Hooks are additionally fully compatible with React Strict Mode and are the recommended way to use Relay at Facebook since mid-2019. The new APIs are however fully compatible with the existing container-based APIs.
Relay is a GraphQL client for React. Relay relies on React components locally declaring their data dependencies. Relay minimizes roundtrips by fetching the data dependencies for all the components in a single GraphQL request. Relay additionally provides APIs that support optimistic updates and synchronize components with their data dependencies.