v3.8.0
Minor Changes
Fetching with Suspense 🎉
-
#10323
64cb88a4b
Thanks @jerelmiller! - Add support for React suspense with a newuseSuspenseQuery
hook.useSuspenseQuery
initiates a network request and causes the component calling it to suspend while the request is in flight. It can be thought of as a drop-in replacement foruseQuery
that allows you to take advantage of React's concurrent features while fetching during render.Consider a
Dog
component that fetches and renders some information about a dog named Mozzarella:View code 🐶
import { Suspense } from 'react'; import { gql, TypedDocumentNode, useSuspenseQuery } from '@apollo/client'; interface Data { dog: { id: string; name: string; }; } interface Variables { name: string; } const GET_DOG_QUERY: TypedDocumentNode<Data, Variables> = gql` query GetDog($name: String) { dog(name: $name) { id name } } `; function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Dog name="Mozzarella" /> </Suspense> ); } function Dog({ name }: { name: string }) { const { data } = useSuspenseQuery(GET_DOG_QUERY, { variables: { name }, }); return <>Name: {data.dog.name}</>; }
For a detailed explanation of
useSuspenseQuery
, see our fetching with Suspense reference. -
#10755
e3c676deb
Thanks @alessbell! - Feature: addsuseBackgroundQuery
anduseReadQuery
hooksuseBackgroundQuery
initiates a request for data in a parent component and returns aQueryReference
which is used to read the data in a child component viauseReadQuery
. If the child component attempts to render before the data can be found in the cache, the child component will suspend until the data is available. On cache updates to watched data, the child component callinguseReadQuery
will re-render with new data but the parent component will not re-render (as it would, for example, if it were usinguseQuery
to issue the request).Consider an
App
component that fetches a list of breeds in the background while also fetching and rendering some information about an individual dog, Mozzarella:View code 🐶
function App() { const [queryRef] = useBackgroundQuery(GET_BREEDS_QUERY); return ( <Suspense fallback={<div>Loading...</div>}> <Dog name="Mozzarella" queryRef={queryRef} /> </Suspense> ); } function Dog({ name, queryRef, }: { name: string; queryRef: QueryReference<BreedData>; }) { const { data } = useSuspenseQuery(GET_DOG_QUERY, { variables: { name }, }); return ( <> Name: {data.dog.name} <Suspense fallback={<div>Loading breeds...</div>}> <Breeds queryRef={queryRef} /> </Suspense> </> ); } function Breeds({ queryRef }: { queryRef: QueryReference<BreedData> }) { const { data } = useReadQuery(queryRef); return data.breeds.map(({ characteristics }) => characteristics.map((characteristic) => ( <div key={characteristic}>{characteristic}</div> )) ); }
For a detailed explanation of
useBackgroundQuery
anduseReadQuery
, see our fetching with Suspense reference.
Document transforms 📑
-
#10509
79df2c7ba
Thanks @jerelmiller! - Add the ability to specify custom GraphQL document transforms. These transforms are run before reading data from the cache, before local state is resolved, and before the query document is sent through the link chain.To register a custom document transform, create a transform using the
DocumentTransform
class and pass it to thedocumentTransform
option onApolloClient
.import { DocumentTransform } from "@apollo/client"; const documentTransform = new DocumentTransform((document) => { // do something with `document` return transformedDocument; }); const client = new ApolloClient({ documentTransform: documentTransform });
For more information on the behavior and API of
DocumentTransform
, see its reference page in our documentation.
New removeTypenameFromVariables
link 🔗
-
#10853
300957960
Thanks @jerelmiller! - Introduce the newremoveTypenameFromVariables
link. This link will automatically remove__typename
fields fromvariables
for all operations. This link can be configured to exclude JSON-scalars for scalars that utilize__typename
.This change undoes some work from #10724 where
__typename
was automatically stripped for all operations with no configuration. This was determined to be a breaking change and therefore moved into this link.For a detailed explanation of
removeTypenameFromVariables
, see its API reference.
New skipToken
sentinel ⏭️
-
#11112
b4aefcfe9
Thanks @jerelmiller! - Adds support for askipToken
sentinel that can be used asoptions
inuseSuspenseQuery
anduseBackgroundQuery
to skip execution of a query. This works identically to theskip
option but is more type-safe and as such, becomes the recommended way to skip query execution. As such, theskip
option has been deprecated in favor ofskipToken
.We are considering the removal of the
skip
option fromuseSuspenseQuery
anduseBackgroundQuery
in the next major. We are releasing with it now to make migration fromuseQuery
easier and makeskipToken
more discoverable.useSuspenseQuery
import { skipToken, useSuspenseQuery } from "@apollo/client"; const id: number | undefined; const { data } = useSuspenseQuery( query, id ? { variables: { id } } : skipToken );
useBackgroundQuery
import { skipToken, useBackgroundQuery } from '@apollo/client'; function Parent() { const [queryRef] = useBackgroundQuery( query, id ? { variables: { id } } : skipToken ); return queryRef ? <Child queryRef={queryRef} /> : null; } function Child({ queryRef }: { queryRef: QueryReference<TData> }) { const { data } = useReadQuery(queryRef); }
For a detailed explanation of
skipToken
, see its API reference.
New error extraction mechanism, smaller bundles 📉
-
#10887
f8c0b965d
Thanks @phryneas! - Add a new mechanism for Error Extraction to reduce bundle size by including error message texts on an opt-in basis.By default, errors will link to an error page with the entire error message.
This replaces "development" and "production" errors and works without
additional bundler configuration.Bundling the text of error messages and development warnings can be enabled as follows:
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev"; if (process.env.NODE_ENV !== "production") { loadErrorMessages(); loadDevMessages(); }
For a detailed explanation, see our reference on reducing bundle size.
New @nonreactive
directive 🎬
-
#10722
c7e60f83d
Thanks @benjamn! - Implement a@nonreactive
directive for selectively skipping reactive comparisons of query result subtrees.The
@nonreactive
directive can be used to mark query fields or fragment spreads and is used to indicate that changes to the data contained within the subtrees marked@nonreactive
should not trigger re-rendering. This allows parent components to fetch data to be rendered by their children without re-rendering themselves when the data corresponding with fields marked as@nonreactive
change.Consider an
App
component that fetches and renders a list of ski trails:View code 🎿
const TrailFragment = gql` fragment TrailFragment on Trail { name status } `; const ALL_TRAILS = gql` query allTrails { allTrails { id ...TrailFragment @nonreactive } } ${TrailFragment} `; function App() { const { data, loading } = useQuery(ALL_TRAILS); return ( <main> <h2>Ski Trails</h2> <ul> {data?.trails.map((trail) => ( <Trail key={trail.id} id={trail.id} /> ))} </ul> </main> ); }
The
Trail
component renders a trail's name and status and allows the user to execute a mutation to toggle the status of the trail between"OPEN"
and"CLOSED"
:View code 🎿
const Trail = ({ id }) => { const [updateTrail] = useMutation(UPDATE_TRAIL); const { data } = useFragment({ fragment: TrailFragment, from: { __typename: "Trail", id, }, }); return ( <li key={id}> {data.name} - {data.status} <input checked={data.status === "OPEN" ? true : false} type="checkbox" onChange={(e) => { updateTrail({ variables: { trailId: id, status: e.target.checked ? "OPEN" : "CLOSED", }, }); }} /> </li> ); };
Notice that the
Trail
component isn't receiving the entiretrail
object via props, only theid
which is used along with the fragment document to create a live binding for each trail item in the cache. This allows eachTrail
component to react to the cache updates for a single trail independently. Updates to a trail'sstatus
will not cause the parentApp
component to rerender since the@nonreactive
directive is applied to theTrailFragment
spread, a fragment that includes thestatus
field.For a detailed explanation, see our
@nonreactive
reference and @alessbell's post on the Apollo blog about using@nonreactive
withuseFragment
.
Abort the AbortController
signal more granularly 🛑
-
#11040
125ef5b2a
Thanks @phryneas! -HttpLink
/BatchHttpLink
: Abort theAbortController
signal more granularly.Before this change, when
HttpLink
/BatchHttpLink
created anAbortController
internally, the signal would always be.abort
ed after the request was completed. This could cause issues with Sentry Session Replay and Next.js App Router Cache invalidations, which just replayed the fetch with the same options - including the cancelledAbortSignal
.With this change, the
AbortController
will only be.abort()
ed by outside events, not as a consequence of the request completing.
useFragment
drops its experimental label 🎓
-
#10916
ea75e18de
Thanks @alessbell! - Remove experimental labels.useFragment
, introduced in3.7.0
asuseFragment_experimental
, is no longer an experimental API 🎉 We've removed the_experimental
suffix from its named export and have made a number of improvements.For a detailed explanation, see our
useFragment
reference and @alessbell's post on the Apollo blog about usinguseFragment
with@nonreactive
for improved performance when rendering lists.useFragment
improvements-
#10765
35f36c5aa
Thanks @phryneas! - More robust types for thedata
property onUseFragmentResult
. When a partial result is given, the type is now correctly set toPartial<TData>
. -
#11083
f766e8305
Thanks @phryneas! - Adjust the rerender timing ofuseQuery
to more closely align withuseFragment
. This means that cache updates delivered to both hooks should trigger renders at relatively the same time. Previously, theuseFragment
might rerender much faster leading to some confusion. -
#10836
6794893c2
Thanks @phryneas! - Remove the deprecatedreturnPartialData
option fromuseFragment
hook.
-
More Minor Changes
-
#10895
e187866fd
Thanks @Gelio! - Add generic type parameter for the entity modified incache.modify
. Improves TypeScript type inference for that type's fields and values of those fields.Example:
cache.modify<Book>({ id: cache.identify(someBook), fields: { title: (title) => { // title has type `string`. // It used to be `any`. }, => { // author has type `Reference | Book["author"]`. // It used to be `any`. }, }, });
-
#10895
e187866fd
Thanks @Gelio! - Use unique opaque types for theDELETE
andINVALIDATE
Apollo cache modifiers.This increases type safety, since these 2 modifiers no longer have the
any
type. Moreover, it no longer triggers the@typescript-eslint/no-unsafe-return
rule. -
#10340
4f73c5ca1
Thanks @alessbell! - Avoid callinguseQuery
onCompleted
for cache writes -
#10527
0cc7e2e19
Thanks @phryneas! - Remove thequery
/mutation
/subscription
option from hooks that already take that value as their first argument. -
#10506
2dc2e1d4f
Thanks @phryneas! - prevent accidental widening of inferredTData
andTVariables
generics for query hook option arguments -
#10521
fbf729414
Thanks @benjamn! - Simplify__DEV__
polyfill to use imports instead of global scope -
#10994
2ebbd3abb
Thanks @phryneas! - Add .js file extensions to imports in src and dist/*/.d.ts -
#11045
9c1d4a104
Thanks @jerelmiller! - When changing variables back to a previously used set of variables, do not automatically cache the result as part of the query reference. Instead, dispose of the query reference so that theInMemoryCache
can determine the cached behavior. This means that fetch policies that would guarantee a network request are now honored when switching back to previously used variables. -
#11058
89bf33c42
Thanks @phryneas! - (Batch)HttpLink: PropagateAbortError
s to the user when a user-providedsignal
is passed to the link. Previously, these links would swallow allAbortErrors
, potentially causing queries and mutations to never resolve. As a result of this change, users are now expected to handleAbortError
s when passing in a user-providedsignal
. -
#10346
3bcfc42d3
Thanks @jerelmiller! - Add the ability to allow@client
fields to be sent to the link chain. -
#10567
c2ce6496c
Thanks @benjamn! - AllowApolloCache
implementations to specify default value forassumeImmutableResults
client option, improving performance for applications currently usingInMemoryCache
without configuringnew ApolloClient({ assumeImmutableResults: true })
-
#10915
3a62d8228
Thanks @phryneas! - Changes how development-only code is bundled in the library to more reliably enable consuming bundlers to reduce production bundle sizes while keeping compatibility with non-node environments.
Patch Changes
-
#11086
0264fee06
Thanks @jerelmiller! - Fix an issue where a call torefetch
,fetchMore
, or changingskip
tofalse
that returned a result deeply equal to data in the cache would get stuck in a pending state and never resolve. -
#11053
c0ca70720
Thanks @phryneas! - AddSuspenseCache
as a lazy hidden property on ApolloClient.
This means thatSuspenseCache
is now an implementation details of Apollo Client and you no longer need to manually instantiate it and no longer need to pass it intoApolloProvider
. Trying to instantiate aSuspenseCache
instance in your code will now throw an error. -
#11115
78739e3ef
Thanks @phryneas! - Enforceexport type
for all type-level exports. -
#11027
e47cfd04e
Thanks @phryneas! - Prevents the DevTool installation warning to be turned into a documentation link. -
#10594
f221b5e8f
Thanks @phryneas! - Add asuspenseCache
option touseSuspenseQuery
-
#10700
12e37f46f
Thanks @jerelmiller! - Add aqueryKey
option touseSuspenseQuery
that allows the hook to create a unique subscription instance. -
#10724
e285dfd00
Thanks @jerelmiller! - Automatically strips__typename
fields fromvariables
sent to the server when usingHttpLink
,BatchHttpLink
, orGraphQLWsLink
. This allows GraphQL data returned from a query to be used as an argument to a subsequent GraphQL operation without the need to strip the__typename
in user-space. -
#10957
445164d21
Thanks @phryneas! - UseReact.version
as key for shared Contexts. -
#10635
7df51ee19
Thanks @jerelmiller! - Fix an issue where cache updates would not propagate touseSuspenseQuery
while in strict mode. -
#11013
5ed2cfdaf
Thanks @alessbell! - Make private fieldsinFlightLinkObservables
andfetchCancelFns
protected in QueryManager in order to make types available in@apollo/experimental-nextjs-app-support
package when extending theApolloClient
class. -
#10869
ba1d06166
Thanks @phryneas! - Ensure Context value stability when rerendering ApolloProvider with the sameclient
and/orsuspenseCache
prop -
#11103
e3d611daf
Thanks @caylahamann! - Fixes a bug inuseMutation
so thatonError
is called when an error is returned from the request witherrorPolicy
set to 'all' . -
#10657
db305a800
Thanks @jerelmiller! - ReturnnetworkStatus
in theuseSuspenseQuery
result. -
#10937
eea44eb87
Thanks @jerelmiller! - MovesDocumentTransform
to theutilities
sub-package to avoid a circular dependency between thecore
andcache
sub-packages. -
#10951
2e833b2ca
Thanks @alessbell! - ImproveuseBackgroundQuery
type interface -
#10651
8355d0e1e
Thanks @jerelmiller! - Fixes an issue whereuseSuspenseQuery
would not respond to cache updates when using a cache-firstfetchPolicy
after the hook was mounted with data already in the cache. -
#11026
b8d405eee
Thanks @phryneas! - Store React.Context instance mapped by React.createContext instance, not React.version.
UsingReact.version
can cause problems withpreact
, as multiple versions ofpreact
will all identify themselves as React17.0.2
. -
#11000
1d43ab616
Thanks @phryneas! - Useimport * as React
everywhere. This prevents an error when importing@apollo/client
in a React Server component. (see #10974) -
#10852
27fbdb3f9
Thanks @phryneas! - Chore: Add ESLint rule for consistent type imports, apply autofix -
#10999
c1904a78a
Thanks @phryneas! - Fix a bug inQueryReference
wherethis.resolve
orthis.reject
might be executed even ifundefined
. -
#10940
1d38f128f
Thanks @jerelmiller! - Add support for theskip
option inuseBackgroundQuery
anduseSuspenseQuery
. Setting this option totrue
will avoid a network request. -
#10672
932252b0c
Thanks @jerelmiller! - Fix the compatibility betweenuseSuspenseQuery
and React'suseDeferredValue
andstartTransition
APIs to allow React to show stale UI while the changes to the variable cause the component to suspend.Breaking change
nextFetchPolicy
support has been removed fromuseSuspenseQuery
. If you are using this option, remove it, otherwise it will be ignored. -
#10964
f33171506
Thanks @alessbell! - Fixes a bug inBatchHttpLink
that removed variables from all requests by default. -
#10633
90a06eeeb
Thanks @benjamn! - Fix type policy inheritance involving fuzzypossibleTypes
-
#10754
64b304862
Thanks @sincraianul! - FixincludeUnusedVariables
option not working withBatchHttpLink
-
#11018
5618953f3
Thanks @jerelmiller! -useBackgroundQuery
now uses its own options type calledBackgroundQueryHookOptions
rather than reusingSuspenseQueryHookOptions
. -
#11035
a3ab7456d
Thanks @jerelmiller! - Incrementally re-render deferred queries after callingrefetch
or settingskip
tofalse
to match the behavior of the initial fetch. Previously, the hook would not re-render until the entire result had finished loading in these cases. -
#10399
652a1ae08
Thanks @alessbell! - Silence useLayoutEffect warning when useSuspenseQuery runs on server -
#10919
f796ce1ac
Thanks @jerelmiller! - Fix an issue when using a link that relied onoperation.getContext
andoperation.setContext
would error out when it was declared after theremoveTypenameFromVariables
link. -
#10968
b102390b2
Thanks @phryneas! - Use printed query for query deduplication. Cacheprint
calls for GraphQL documents to speed up repeated operations. -
#11071
4473e925a
Thanks @jerelmiller! - #10509 introduced some helpers for determining the type of operation for a GraphQL query. This imported theOperationTypeNode
from graphql-js which is not available in GraphQL 14. To maintain compatibility with graphql-js v14, this has been reverted to use plain strings. -
#10766
ffb179e55
Thanks @jerelmiller! - More robust typings for thedata
property returned fromuseSuspenseQuery
when usingreturnPartialData: true
or anerrorPolicy
ofall
orignore
.TData
now defaults tounknown
instead ofany
. -
#10401
3e5b41a75
Thanks @jerelmiller! - Always throw network errors inuseSuspenseQuery
regardless of the seterrorPolicy
. -
#10877
f40248598
Thanks @phryneas! - Change an import inuseQuery
anduseMutation
that added an unnecessary runtime dependency on@apollo/client/core
. This drastically reduces the bundle size of each the hooks. -
#10656
54c4d2f3c
Thanks @jerelmiller! - Ensurerefetch
,fetchMore
, andsubscribeToMore
functions returned byuseSuspenseQuery
are referentially stable between renders, even asdata
is updated. -
#10324
95eb228be
Thanks @jerelmiller! - Add@defer
support touseSuspenseQuery
. -
#10888
1562a2f5a
Thanks @alessbell! - Updates dependency versions inpackage.json
by bumping:@wry/context
to^0.7.3
@wry/equality
to^0.5.6
@wry/trie
to^0.4.3
optimism
to^0.17.4
to 1. fix sourcemap warnings and 2. a Codesandbox sandpack (in-browser) bundler transpilation bug with an upstream optimism workaround.
-
#11010
1051a9c88
Thanks @alessbell! - Hide queryRef in a Symbol inuseBackgroundQuery
s return value. -
#10758
9def7421f
Thanks @phryneas! - useReact.use
where available -
#11032
6a4da900a
Thanks @jerelmiller! - Throw errors inuseSuspenseQuery
for errors returned in incremental chunks whenerrorPolicy
isnone
. This provides a more consistent behavior of theerrorPolicy
in the hook.Potentially breaking change
Previously, if you issued a query with
@defer
and relied onerrorPolicy: 'none'
to set theerror
property returned fromuseSuspenseQuery
when the error was returned in an incremental chunk, this error is now thrown. Switch theerrorPolicy
toall
to avoid throwing the error and instead return it in theerror
property. -
#10960
ee407ef97
Thanks @alessbell! - Adds support forreturnPartialData
andrefetchWritePolicy
options inuseBackgroundQuery
hook. -
#10809
49d28f764
Thanks @jerelmiller! - Fixed the ability to userefetch
andfetchMore
with React'sstartTransition
. The hook will now behave correctly by allowing React to avoid showing the Suspense fallback when these functions are wrapped bystartTransition
. This change deprecates thesuspensePolicy
option in favor ofstartTransition
. -
#11082
0f1cde3a2
Thanks @phryneas! - Restore Apollo Client 3.7getApolloContext
behaviour -
#10969
525a9317a
Thanks @phryneas! - Slightly decrease bundle size and memory footprint ofSuspenseCache
by changing how cache entries are stored internally. -
#11025
6092b6edf
Thanks @jerelmiller! -useSuspenseQuery
anduseBackgroundQuery
will now properly apply changes to its options between renders. -
#10872
96b4f8837
Thanks @phryneas! - The "per-React-Version-Singleton" ApolloContext is now stored onglobalThis
, notReact.createContext
, and throws an error message when accessed from React Server Components. -
#10450
f8bc33387
Thanks @jerelmiller! - Add support for thesubscribeToMore
andclient
fields returned in theuseSuspenseQuery
result.