I’m working on a large project that is currently running into some friction when using useFragment with types that implement interfaces.
Context:
Our app preloads data in a custom package and components then read that data via useFragment. For non-interface types, this is straightforward since the __typename is predictable. But when the fragment is on an interface, the component doesn’t always know which concrete __typename to pass in. We can’t reliably prop-drill the __typename from a parent component, because the data preloading happens in a separate package and is decoupled from the component tree that renders the fragments.
We’ve seen a couple issues in the past related to useFragment and interfaces:
- useFragment fails to get data from cache for an Interface, even when possibleTypes is setup correctly · Issue #11583 · apollographql/apollo-client · GitHub
- Cache redirects don't work with interfaces specified by possibleTypes · Issue #382 · apollographql/apollo-feature-requests · GitHub
We created a minimal repro that emulates our app’s data loading and some of the options we’re considering below: GitHub - jcostello93/apollo-client-fragment-interface
Option 1: Use cache-only useQuery instead of useFragment in leaf nodes. The useQuery hook correctly returns data without requiring the concrete __typename. However, the performance concerns of using useQuery instead of useFragment make this a non-starter.
Option 2: Store cache refs in React context. After the preload, we’d have single cache-only useQuery to read the ids and __typenames from the cache and then store them in context. Components could then get the cache ref from context and pass it to useFragment.
Option 3: Create a useInterfaceFragment helper that checks the cache for the concrete __typename and passes that into useFragment.
Option 4: Define a keyFields function on the interface that hardcodes the interface __typename. For example:
BaseItem: {
keyFields: () => `BaseItem:${id}`,
}
This way, the policy is inherited by implementation types via possibleTypes and components can just pass in from: { id, __typename: ‘BaseItem’}. This workaround was also used in Cache redirects don't work with interfaces specified by possibleTypes · Issue #382 · apollographql/apollo-feature-requests · GitHub.
We’re currently leaning towards Option 4 (custom keyFields on the interface type) given its simplicity and minimal impact on our codebase. But since this breaks from default keyFields behavior, we wanted to validate this pattern with the team and see if we missed any alternatives. We also noticed at least example of strange behavior where keyFields was invoked with an undefined id, leading to a InterfaceType:undefined cache ref.
We also wanted to ask if there’s a plan to support possibleTypes relations in useFragment in the future so the concrete typename is not required.
Thank you!