facebook / facebook/relay

Support code-splitting mutation operations with useMutation

Open
#4,827 2 comments 2 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
19k
Forks
1.9k
PR merge metrics
No merged PRs in 30d

Description

Almost all mutations are not needed for the initial render of a page, often being triggered by some delayed user interaction like a button click. However, it is currently hard to avoid bloating bundles with the mutation nodes when using the built-in `useMutation` hook. The mutation nodes contain JS proportional to the mutation document size (and thus potentially proportional to the page size if the page's data is included as a fragment) in the query text and operation/fragment fields.

There's two main problems I've run into trying to solve this:
1. useMutation requires a ConcreteRequest (GraphQLTaggedNode) as an argument. Since hooks cannot be called conditionally, this means the operation must be imported before the component using the hook can render. This makes it tricky to code-split the mutation's node from the component which triggers it. A possible fix here is to accept a Promise/JSResource for the node in the hook. When the commit function is called, it would chain off the promise/resource (potentially delaying the mutation request slightly if the resource hasn't resolved). This resource could be optimistically prefetched, or deferred until the user interacts (likely up to the caller to decide based on the tradeoff of mutation likelihood and desired speed of issuing the mutation)
2. While it is possible to use [persisted queries](https://relay.dev/docs/guides/persisted-queries/) to avoid including the entire operation text in the bundle, the ConcreteRequest's fragment/operation fields still contains the AST of the mutation. This is inevitable to parse the response of the mutation, but not to kick off the mutation. The @preloadable annotation does allow generating a separate file containing just the parameters needed to issue the request, which can already be used to decrease bundle sizes with useQueryLoader+usePreloadedQuery. A possible fix here is to [extend support to mutations](https://github.com/facebook/relay/pull/4515#issuecomment-1962850888), and add a "useDeferredMutation" hook which accepts Mutation$Parameters (`PreloadableConcreteRequest`), as well as a promise/JSResource for the actual mutation node (similar to `useQueryLoader`, or perhaps `useEntryPointLoader`)

While it would be possible to roll my own version of useMutation which allows this (the logic doesn't seem too complicated), I think it would be beneficial to provide first-class support to encourage this pattern. I'd be happy to help contribute something for this, but would like some confirmation that these are the right paths to go down

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.