@@ -4,22 +4,41 @@ import { SWRGlobalState } from './global-state'
44import { getTimestamp } from './timestamp'
55import * as revalidateEvents from '../constants'
66import {
7- Key ,
87 Cache ,
98 MutatorCallback ,
109 MutatorOptions ,
1110 GlobalState ,
12- State
11+ State ,
12+ Arguments ,
13+ Key
1314} from '../types'
1415
15- export const internalMutate = async < Data > (
16+ type KeyFilter = ( key ?: Arguments ) => boolean
17+ type MutateState < Data > = State < Data , any > & {
18+ // The previously committed data.
19+ _c ?: Data
20+ }
21+
22+ export async function internalMutate < Data > (
23+ cache : Cache ,
24+ _key : KeyFilter ,
25+ _data ?: Data | Promise < Data | undefined > | MutatorCallback < Data > ,
26+ _opts ?: boolean | MutatorOptions < Data >
27+ ) : Promise < Array < Data | undefined > >
28+ export async function internalMutate < Data > (
29+ cache : Cache ,
30+ _key : Arguments ,
31+ _data ?: Data | Promise < Data | undefined > | MutatorCallback < Data > ,
32+ _opts ?: boolean | MutatorOptions < Data >
33+ ) : Promise < Data | undefined >
34+ export async function internalMutate < Data > (
1635 ...args : [
17- Cache ,
18- Key ,
19- undefined | Data | Promise < Data | undefined > | MutatorCallback < Data > ,
20- undefined | boolean | MutatorOptions < Data >
36+ cache : Cache ,
37+ _key : KeyFilter | Arguments ,
38+ _data ?: Data | Promise < Data | undefined > | MutatorCallback < Data > ,
39+ _opts ?: boolean | MutatorOptions < Data >
2140 ]
22- ) : Promise < Data | undefined > = > {
41+ ) : Promise < any > {
2342 const [ cache , _key , _data , _opts ] = args
2443
2544 // When passing as a boolean, it's explicitly used to disable/enable
@@ -35,130 +54,144 @@ export const internalMutate = async <Data>(
3554 const revalidate = options . revalidate !== false
3655 const rollbackOnError = options . rollbackOnError !== false
3756
38- // Serialize key
39- const [ key ] = serialize ( _key )
40- if ( ! key ) return
41-
42- const [ get , set ] = createCacheHelper <
43- Data ,
44- State < Data , any > & {
45- // The previously committed data.
46- _c ?: Data
47- }
48- > ( cache , key )
49- const [ EVENT_REVALIDATORS , MUTATION , FETCH ] = SWRGlobalState . get (
50- cache
51- ) as GlobalState
52-
53- const revalidators = EVENT_REVALIDATORS [ key ]
54- const startRevalidate = ( ) => {
55- if ( revalidate ) {
56- // Invalidate the key by deleting the concurrent request markers so new
57- // requests will not be deduped.
58- delete FETCH [ key ]
59- if ( revalidators && revalidators [ 0 ] ) {
60- return revalidators [ 0 ] ( revalidateEvents . MUTATE_EVENT ) . then (
61- ( ) => get ( ) . data
62- )
57+ // If the second argument is a key filter, return the mutation results for all
58+ // filtered keys.
59+ if ( isFunction ( _key ) ) {
60+ const keyFilter = _key
61+ const matchedKeys : Key [ ] = [ ]
62+ for ( const key of cache . keys ( ) ) {
63+ if (
64+ // Skip the speical useSWRInfinite keys.
65+ ! key . startsWith ( '$inf$' ) &&
66+ keyFilter ( ( cache . get ( key ) as { _k : Arguments } ) . _k )
67+ ) {
68+ matchedKeys . push ( key )
6369 }
6470 }
65- return get ( ) . data
71+ return Promise . all ( matchedKeys . map ( mutateByKey ) )
6672 }
6773
68- // If there is no new data provided, revalidate the key with current state.
69- if ( args . length < 3 ) {
70- // Revalidate and broadcast state.
71- return startRevalidate ( )
72- }
74+ return mutateByKey ( _key )
75+
76+ async function mutateByKey ( _k : Key ) : Promise < Data | undefined > {
77+ // Serialize key
78+ const [ key ] = serialize ( _k )
79+ if ( ! key ) return
80+ const [ get , set ] = createCacheHelper < Data , MutateState < Data > > ( cache , key )
81+ const [ EVENT_REVALIDATORS , MUTATION , FETCH ] = SWRGlobalState . get (
82+ cache
83+ ) as GlobalState
84+
85+ const revalidators = EVENT_REVALIDATORS [ key ]
86+ const startRevalidate = ( ) => {
87+ if ( revalidate ) {
88+ // Invalidate the key by deleting the concurrent request markers so new
89+ // requests will not be deduped.
90+ delete FETCH [ key ]
91+ if ( revalidators && revalidators [ 0 ] ) {
92+ return revalidators [ 0 ] ( revalidateEvents . MUTATE_EVENT ) . then (
93+ ( ) => get ( ) . data
94+ )
95+ }
96+ }
97+ return get ( ) . data
98+ }
7399
74- let data : any = _data
75- let error : unknown
100+ // If there is no new data provided, revalidate the key with current state.
101+ if ( args . length < 3 ) {
102+ // Revalidate and broadcast state.
103+ return startRevalidate ( )
104+ }
76105
77- // Update global timestamps.
78- const beforeMutationTs = getTimestamp ( )
79- MUTATION [ key ] = [ beforeMutationTs , 0 ]
106+ let data : any = _data
107+ let error : unknown
80108
81- const hasOptimisticData = ! isUndefined ( optimisticData )
82- const state = get ( )
109+ // Update global timestamps.
110+ const beforeMutationTs = getTimestamp ( )
111+ MUTATION [ key ] = [ beforeMutationTs , 0 ]
83112
84- // `displayedData` is the current value on screen. It could be the optimistic value
85- // that is going to be overridden by a `committedData`, or get reverted back.
86- // `committedData` is the validated value that comes from a fetch or mutation.
87- const displayedData = state . data
88- const committedData = isUndefined ( state . _c ) ? displayedData : state . _c
113+ const hasOptimisticData = ! isUndefined ( optimisticData )
114+ const state = get ( )
89115
90- // Do optimistic data update.
91- if ( hasOptimisticData ) {
92- optimisticData = isFunction ( optimisticData )
93- ? optimisticData ( committedData )
94- : optimisticData
116+ // `displayedData` is the current value on screen. It could be the optimistic value
117+ // that is going to be overridden by a `committedData`, or get reverted back.
118+ // `committedData` is the validated value that comes from a fetch or mutation.
119+ const displayedData = state . data
120+ const committedData = isUndefined ( state . _c ) ? displayedData : state . _c
95121
96- // When we set optimistic data, backup the current committedData data in `_c`.
97- set ( { data : optimisticData , _c : committedData } )
98- }
122+ // Do optimistic data update.
123+ if ( hasOptimisticData ) {
124+ optimisticData = isFunction ( optimisticData )
125+ ? optimisticData ( committedData )
126+ : optimisticData
99127
100- if ( isFunction ( data ) ) {
101- // `data` is a function, call it passing current cache value.
102- try {
103- data = ( data as MutatorCallback < Data > ) ( committedData )
104- } catch ( err ) {
105- // If it throws an error synchronously, we shouldn't update the cache.
106- error = err
128+ // When we set optimistic data, backup the current committedData data in `_c`.
129+ set ( { data : optimisticData , _c : committedData } )
107130 }
108- }
109131
110- // `data` is a promise/thenable, resolve the final data first.
111- if ( data && isFunction ( ( data as Promise < Data > ) . then ) ) {
112- // This means that the mutation is async, we need to check timestamps to
113- // avoid race conditions.
114- data = await ( data as Promise < Data > ) . catch ( err => {
115- error = err
116- } )
117-
118- // Check if other mutations have occurred since we've started this mutation.
119- // If there's a race we don't update cache or broadcast the change,
120- // just return the data.
121- if ( beforeMutationTs !== MUTATION [ key ] [ 0 ] ) {
122- if ( error ) throw error
123- return data
124- } else if ( error && hasOptimisticData && rollbackOnError ) {
125- // Rollback. Always populate the cache in this case but without
126- // transforming the data.
127- populateCache = true
128- data = committedData
129-
130- // Reset data to be the latest committed data, and clear the `_c` value.
131- set ( { data, _c : UNDEFINED } )
132+ if ( isFunction ( data ) ) {
133+ // `data` is a function, call it passing current cache value.
134+ try {
135+ data = ( data as MutatorCallback < Data > ) ( committedData )
136+ } catch ( err ) {
137+ // If it throws an error synchronously, we shouldn't update the cache.
138+ error = err
139+ }
132140 }
133- }
134141
135- // If we should write back the cache after request.
136- if ( populateCache ) {
137- if ( ! error ) {
138- // Transform the result into data.
139- if ( isFunction ( populateCache ) ) {
140- data = populateCache ( data , committedData )
142+ // `data` is a promise/thenable, resolve the final data first.
143+ if ( data && isFunction ( ( data as Promise < Data > ) . then ) ) {
144+ // This means that the mutation is async, we need to check timestamps to
145+ // avoid race conditions.
146+ data = await ( data as Promise < Data > ) . catch ( err => {
147+ error = err
148+ } )
149+
150+ // Check if other mutations have occurred since we've started this mutation.
151+ // If there's a race we don't update cache or broadcast the change,
152+ // just return the data.
153+ if ( beforeMutationTs !== MUTATION [ key ] [ 0 ] ) {
154+ if ( error ) throw error
155+ return data
156+ } else if ( error && hasOptimisticData && rollbackOnError ) {
157+ // Rollback. Always populate the cache in this case but without
158+ // transforming the data.
159+ populateCache = true
160+ data = committedData
161+
162+ // Reset data to be the latest committed data, and clear the `_c` value.
163+ set ( { data, _c : UNDEFINED } )
141164 }
142-
143- // Only update cached data if there's no error. Data can be `undefined` here.
144- set ( { data, _c : UNDEFINED } )
145165 }
146166
147- // Always update error and original data here.
148- set ( { error } )
149- }
167+ // If we should write back the cache after request.
168+ if ( populateCache ) {
169+ if ( ! error ) {
170+ // Transform the result into data.
171+ if ( isFunction ( populateCache ) ) {
172+ data = populateCache ( data , committedData )
173+ }
150174
151- // Reset the timestamp to mark the mutation has ended.
152- MUTATION [ key ] [ 1 ] = getTimestamp ( )
175+ // Only update cached data if there's no error. Data can be `undefined` here.
176+ set ( { data, _c : UNDEFINED } )
177+ }
153178
154- // Update existing SWR Hooks' internal states:
155- const res = await startRevalidate ( )
179+ // Always update error and original data here.
180+ set ( { error } )
181+ }
182+
183+ // Reset the timestamp to mark the mutation has ended.
184+ MUTATION [ key ] [ 1 ] = getTimestamp ( )
156185
157- // The mutation and revalidation are ended, we can clear it since the data is
158- // not an optimistic value anymore.
159- set ( { _c : UNDEFINED } )
186+ // Update existing SWR Hooks' internal states:
187+ const res = await startRevalidate ( )
160188
161- // Throw error or return data
162- if ( error ) throw error
163- return populateCache ? res : data
189+ // The mutation and revalidation are ended, we can clear it since the data is
190+ // not an optimistic value anymore.
191+ set ( { _c : UNDEFINED } )
192+
193+ // Throw error or return data
194+ if ( error ) throw error
195+ return populateCache ? res : data
196+ }
164197}
0 commit comments