Skip to content

Commit 0be5d9b

Browse files
committed
feat: new context params in event hook handlers
Allow easy access to the client, especially useful with useSubscription().
1 parent c9b648d commit 0be5d9b

File tree

8 files changed

+118
-35
lines changed

8 files changed

+118
-35
lines changed

packages/docs/src/api/use-mutation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@
4747

4848
- `called`: boolean `Ref` holding `true` if the mutation was already called.
4949

50-
- `onDone`: Event hook called when the mutation successfully completes.
50+
- `onDone(handler)`: Event hook called when the mutation successfully completes. Handler is called with: `result` (mutation result) and `context` which is an object with `client` (ApolloClient instance).
5151

52-
- `onError`: Event hook called when an error occurs.
52+
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).

packages/docs/src/api/use-query.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
- `subscribeToMore(options)`: Add a subscription to the query, useful to add new data received from the server in real-time. See [Subscription](../guide-composable/subscription#subscribetomore).
6262

63-
- `onResult(handler)`: Event hook called when a new result is available.
63+
- `onResult(handler)`: Event hook called when a new result is available. Handler is called with: `result` (query result) and `context` which is an object with `client` (ApolloClient instance).
6464

65-
- `onError(handler)`: Event hook called when an error occurs.
65+
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
6666

packages/docs/src/api/use-subscription.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
- `variables`: Ref holding the variables object.
3535

36-
- `onResult(handler)`: Event hook called when a new result is available.
36+
- `onResult(handler)`: Event hook called when a new result is available. Handler is called with: `result` (new result) and `context` which is an object with `client` (ApolloClient instance).
3737

38-
- `onError(handler)`: Event hook called when an error occurs.
38+
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
3939

packages/docs/src/guide-composable/subscription.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ This is called when a new result is received from the server:
403403
```js
404404
const { onResult } = useSubscription(...)
405405

406-
onResult(result => {
406+
onResult((result, context) => {
407407
console.log(result.data)
408408
})
409409
```
@@ -417,11 +417,48 @@ import { logErrorMessages } from '@vue/apollo-util'
417417

418418
const { onError } = useSubscription(...)
419419

420-
onError(error => {
420+
onError((error, context) => {
421421
logErrorMessages(error)
422422
})
423423
```
424424

425+
### Update the cache
426+
427+
Using `onResult`, you can update the Apollo cache with the new data:
428+
429+
```js
430+
const { onResult } = useSubscription(...)
431+
432+
onResult((result, { client }) => {
433+
const query = {
434+
query: gql`query getMessages ($channelId: ID!) {
435+
messages(channelId: $channelId) {
436+
id
437+
text
438+
}
439+
}`,
440+
variables: {
441+
channelId: '123',
442+
},
443+
}
444+
445+
// Read the query
446+
let data = client.readQuery(query)
447+
448+
// Update cached data
449+
data = {
450+
...data,
451+
messages: [...data.messages, result.data.messageAdded],
452+
}
453+
454+
// Write back the new result for the query
455+
client.writeQuery({
456+
...query,
457+
data,
458+
})
459+
})
460+
```
461+
425462
## subscribeToMore
426463

427464
With GraphQL subscriptions your client will be alerted on push from the server and you should choose the pattern that fits your application the most:

packages/vue-apollo-composable/src/useMutation.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DocumentNode } from 'graphql'
2-
import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode, ApolloError } from '@apollo/client/core/index.js'
2+
import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode, ApolloError, ApolloClient } from '@apollo/client/core/index.js'
33
import { ref, onBeforeUnmount, isRef, Ref, getCurrentInstance } from 'vue-demi'
44
import { useApolloClient } from './useApolloClient'
55
import { ReactiveFunction } from './util/ReactiveFunction'
@@ -25,15 +25,23 @@ export type MutateOverrideOptions<TResult> = Pick<UseMutationOptions<TResult, Op
2525
export type MutateResult<TResult> = Promise<FetchResult<TResult, Record<string, any>, Record<string, any>> | null>
2626
export type MutateFunction<TResult, TVariables> = (variables?: TVariables | null, overrideOptions?: MutateOverrideOptions<TResult>) => MutateResult<TResult>
2727

28+
export interface OnDoneContext {
29+
client: ApolloClient<any>
30+
}
31+
32+
export interface OnErrorContext {
33+
client: ApolloClient<any>
34+
}
35+
2836
export interface UseMutationReturn<TResult, TVariables> {
2937
mutate: MutateFunction<TResult, TVariables>
3038
loading: Ref<boolean>
3139
error: Ref<ApolloError | null>
3240
called: Ref<boolean>
33-
onDone: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
41+
onDone: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>, context: OnDoneContext) => void) => {
3442
off: () => void
3543
}
36-
onError: (fn: (param: ApolloError) => void) => {
44+
onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {
3745
off: () => void
3846
}
3947
}
@@ -51,8 +59,8 @@ export function useMutation<
5159
const error = ref<ApolloError | null>(null)
5260
const called = ref<boolean>(false)
5361

54-
const doneEvent = useEventHook<FetchResult<TResult, Record<string, any>, Record<string, any>>>()
55-
const errorEvent = useEventHook<ApolloError>()
62+
const doneEvent = useEventHook<[FetchResult<TResult, Record<string, any>, Record<string, any>>, OnDoneContext]>()
63+
const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()
5664

5765
// Apollo Client
5866
const { resolveClient } = useApolloClient()
@@ -92,13 +100,17 @@ export function useMutation<
92100
: undefined,
93101
})
94102
loading.value = false
95-
doneEvent.trigger(result)
103+
doneEvent.trigger(result, {
104+
client,
105+
})
96106
return result
97107
} catch (e) {
98108
const apolloError = toApolloError(e)
99109
error.value = apolloError
100110
loading.value = false
101-
errorEvent.trigger(apolloError)
111+
errorEvent.trigger(apolloError, {
112+
client,
113+
})
102114
if (currentOptions.throws === 'always' || (currentOptions.throws !== 'never' && !errorEvent.getCount())) {
103115
throw apolloError
104116
}

packages/vue-apollo-composable/src/useQuery.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {
2222
ObservableSubscription,
2323
TypedDocumentNode,
2424
ApolloError,
25+
ApolloClient,
2526
} from '@apollo/client/core/index.js'
2627
import { throttle, debounce } from 'throttle-debounce'
2728
import { useApolloClient } from './useApolloClient'
@@ -58,6 +59,14 @@ export type DocumentParameter<TResult, TVariables> = DocumentNode | Ref<Document
5859
export type VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>
5960
export type OptionsParameter<TResult, TVariables extends OperationVariables> = UseQueryOptions<TResult, TVariables> | Ref<UseQueryOptions<TResult, TVariables>> | ReactiveFunction<UseQueryOptions<TResult, TVariables>>
6061

62+
export interface OnResultContext {
63+
client: ApolloClient<any>
64+
}
65+
66+
export interface OnErrorContext {
67+
client: ApolloClient<any>
68+
}
69+
6170
// Return
6271
export interface UseQueryReturn<TResult, TVariables extends OperationVariables> {
6372
result: Ref<TResult | undefined>
@@ -75,10 +84,10 @@ export interface UseQueryReturn<TResult, TVariables extends OperationVariables>
7584
refetch: (variables?: TVariables) => Promise<ApolloQueryResult<TResult>> | undefined
7685
fetchMore: (options: FetchMoreQueryOptions<TVariables, TResult> & FetchMoreOptions<TResult, TVariables>) => Promise<ApolloQueryResult<TResult>> | undefined
7786
subscribeToMore: <TSubscriptionVariables = OperationVariables, TSubscriptionData = TResult>(options: SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData> | Ref<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>> | ReactiveFunction<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>>) => void
78-
onResult: (fn: (param: ApolloQueryResult<TResult>) => void) => {
87+
onResult: (fn: (param: ApolloQueryResult<TResult>, context: OnResultContext) => void) => {
7988
off: () => void
8089
}
81-
onError: (fn: (param: ApolloError) => void) => {
90+
onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {
8291
off: () => void
8392
}
8493
}
@@ -157,9 +166,9 @@ export function useQueryImpl<
157166
* Result from the query
158167
*/
159168
const result = ref<TResult | undefined>()
160-
const resultEvent = useEventHook<ApolloQueryResult<TResult>>()
169+
const resultEvent = useEventHook<[ApolloQueryResult<TResult>, OnResultContext]>()
161170
const error = ref<ApolloError | null>(null)
162-
const errorEvent = useEventHook<ApolloError>()
171+
const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()
163172

164173
// Loading
165174

@@ -217,6 +226,10 @@ export function useQueryImpl<
217226
// Apollo Client
218227
const { resolveClient } = useApolloClient()
219228

229+
function getClient () {
230+
return resolveClient(currentOptions.value?.clientId)
231+
}
232+
220233
// Query
221234

222235
const query: Ref<ObservableQuery<TResult, TVariables> | null | undefined> = shallowRef()
@@ -242,7 +255,7 @@ export function useQueryImpl<
242255
error.value = null
243256
loading.value = true
244257

245-
const client = resolveClient(currentOptions.value?.clientId)
258+
const client = getClient()
246259

247260
query.value = client.watchQuery<TResult, TVariables>({
248261
query: currentDocument,
@@ -328,7 +341,9 @@ export function useQueryImpl<
328341
networkStatus.value = queryResult.networkStatus
329342
// Wait for handlers to be registered
330343
nextTick(() => {
331-
resultEvent.trigger(queryResult)
344+
resultEvent.trigger(queryResult, {
345+
client: getClient(),
346+
})
332347
})
333348
}
334349

@@ -357,7 +372,9 @@ export function useQueryImpl<
357372
networkStatus.value = 8
358373
// Wait for handlers to be registered
359374
nextTick(() => {
360-
errorEvent.trigger(apolloError)
375+
errorEvent.trigger(apolloError, {
376+
client: getClient(),
377+
})
361378
})
362379
}
363380

packages/vue-apollo-composable/src/useSubscription.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
ObservableSubscription,
1818
TypedDocumentNode,
1919
ApolloError,
20+
ApolloClient,
2021
} from '@apollo/client/core/index.js'
2122
import { throttle, debounce } from 'throttle-debounce'
2223
import { ReactiveFunction } from './util/ReactiveFunction'
@@ -44,6 +45,14 @@ type DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode> |
4445
type VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>
4546
type OptionsParameter<TResult, TVariables> = UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>> | ReactiveFunction<UseSubscriptionOptions<TResult, TVariables>>
4647

48+
export interface OnResultContext {
49+
client: ApolloClient<any>
50+
}
51+
52+
export interface OnErrorContext {
53+
client: ApolloClient<any>
54+
}
55+
4756
export interface UseSubscriptionReturn<TResult, TVariables> {
4857
result: Ref<TResult | null | undefined>
4958
loading: Ref<boolean>
@@ -55,10 +64,10 @@ export interface UseSubscriptionReturn<TResult, TVariables> {
5564
variables: Ref<TVariables | undefined>
5665
options: UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>>
5766
subscription: Ref<Observable<FetchResult<TResult, Record<string, any>, Record<string, any>>> | null>
58-
onResult: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
67+
onResult: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>, context: OnResultContext) => void) => {
5968
off: () => void
6069
}
61-
onError: (fn: (param: ApolloError) => void) => {
70+
onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {
6271
off: () => void
6372
}
6473
}
@@ -119,9 +128,9 @@ export function useSubscription <
119128
const optionsRef = paramToReactive(options)
120129

121130
const result = ref<TResult | null | undefined>()
122-
const resultEvent = useEventHook<FetchResult<TResult>>()
131+
const resultEvent = useEventHook<[FetchResult<TResult>, OnResultContext]>()
123132
const error = ref<ApolloError | null>(null)
124-
const errorEvent = useEventHook<ApolloError>()
133+
const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()
125134

126135
const loading = ref(false)
127136
vm && trackSubscription(loading)
@@ -133,12 +142,16 @@ export function useSubscription <
133142
let observer: ObservableSubscription | null = null
134143
let started = false
135144

145+
function getClient () {
146+
return resolveClient(currentOptions.value?.clientId)
147+
}
148+
136149
function start () {
137150
if (started || !isEnabled.value || isServer) return
138151
started = true
139152
loading.value = true
140153

141-
const client = resolveClient(currentOptions.value?.clientId)
154+
const client = getClient()
142155

143156
subscription.value = client.subscribe<TResult, TVariables>({
144157
query: currentDocument,
@@ -155,15 +168,19 @@ export function useSubscription <
155168
function onNextResult (fetchResult: FetchResult<TResult>) {
156169
result.value = fetchResult.data
157170
loading.value = false
158-
resultEvent.trigger(fetchResult)
171+
resultEvent.trigger(fetchResult, {
172+
client: getClient(),
173+
})
159174
}
160175

161176
function onError (fetchError: unknown) {
162177
const apolloError = toApolloError(fetchError)
163178

164179
error.value = apolloError
165180
loading.value = false
166-
errorEvent.trigger(apolloError)
181+
errorEvent.trigger(apolloError, {
182+
client: getClient(),
183+
})
167184
}
168185

169186
function stop () {

packages/vue-apollo-composable/src/util/useEventHook.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
export function useEventHook<TParam = any> () {
2-
const fns: Array<(param: TParam) => void> = []
1+
export function useEventHook<TParams extends any[] = any[]> () {
2+
const fns: Array<(...params: TParams) => void> = []
33

4-
function on (fn: (param: TParam) => void) {
4+
function on (fn: (...params: TParams) => void) {
55
fns.push(fn)
66
return {
77
off: () => off(fn),
88
}
99
}
1010

11-
function off (fn: (param: TParam) => void) {
11+
function off (fn: (...params: TParams) => void) {
1212
const index = fns.indexOf(fn)
1313
if (index !== -1) {
1414
fns.splice(index, 1)
1515
}
1616
}
1717

18-
function trigger (param: TParam) {
18+
function trigger (...params: TParams) {
1919
for (const fn of fns) {
20-
fn(param)
20+
fn(...params)
2121
}
2222
}
2323

0 commit comments

Comments
 (0)