Skip to content

Commit 7cef58d

Browse files
huozhishuding
andcommitted
feat: support functional optimisticData (#1861)
* feat: support functional optimisticData * Update src/types.ts Co-authored-by: Shu Ding <[email protected]>
1 parent 1ae8cc6 commit 7cef58d

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export type MutatorCallback<Data = any> = (
145145
export type MutatorOptions<Data = any> = {
146146
revalidate?: boolean
147147
populateCache?: boolean | ((result: any, currentData: Data) => Data)
148-
optimisticData?: Data
148+
optimisticData?: Data | ((currentData?: Data) => Data)
149149
rollbackOnError?: boolean
150150
}
151151

src/utils/mutate.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const internalMutate = async <Data>(
2727
: options.populateCache
2828
const revalidate = options.revalidate !== false
2929
const rollbackOnError = options.rollbackOnError !== false
30-
const optimisticData = options.optimisticData
30+
const customOptimisticData = options.optimisticData
3131

3232
// Serilaize key
3333
const [key, , keyInfo] = serialize(_key)
@@ -55,11 +55,14 @@ export const internalMutate = async <Data>(
5555
// Update global timestamps.
5656
const beforeMutationTs = getTimestamp()
5757
MUTATION[key] = [beforeMutationTs, 0]
58-
const hasOptimisticData = !isUndefined(optimisticData)
58+
const hasCustomOptimisticData = !isUndefined(customOptimisticData)
5959
const rollbackData = cache.get(key)
6060

6161
// Do optimistic data update.
62-
if (hasOptimisticData) {
62+
if (hasCustomOptimisticData) {
63+
const optimisticData = isFunction(customOptimisticData)
64+
? customOptimisticData(rollbackData)
65+
: customOptimisticData
6366
cache.set(key, optimisticData)
6467
broadcastState(cache, key, optimisticData)
6568
}
@@ -88,7 +91,7 @@ export const internalMutate = async <Data>(
8891
if (beforeMutationTs !== MUTATION[key][0]) {
8992
if (error) throw error
9093
return data
91-
} else if (error && hasOptimisticData && rollbackOnError) {
94+
} else if (error && hasCustomOptimisticData && rollbackOnError) {
9295
// Rollback. Always populate the cache in this case but without
9396
// transforming the data.
9497
populateCache = true

test/use-swr-local-mutation.test.tsx

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,72 @@ describe('useSWR - local mutation', () => {
10381038
expect(renderedData).toEqual([undefined, 'foo', 'bar', 'baz', 'foo'])
10391039
})
10401040

1041+
it('should support optimistic updates via functional `optimisticData`', async () => {
1042+
const key = createKey()
1043+
const renderedData = []
1044+
let mutate
1045+
1046+
function Page() {
1047+
const { data, mutate: boundMutate } = useSWR(key, () =>
1048+
createResponse('foo', { delay: 20 })
1049+
)
1050+
mutate = boundMutate
1051+
renderedData.push(data)
1052+
return <div>data: {String(data)}</div>
1053+
}
1054+
1055+
renderWithConfig(<Page />)
1056+
await screen.findByText('data: foo')
1057+
1058+
await act(() =>
1059+
mutate(createResponse('baz', { delay: 20 }), {
1060+
optimisticData: data => 'functional_' + data
1061+
})
1062+
)
1063+
await sleep(30)
1064+
expect(renderedData).toEqual([
1065+
undefined,
1066+
'foo',
1067+
'functional_foo',
1068+
'baz',
1069+
'foo'
1070+
])
1071+
})
1072+
1073+
it('should be able use mutate to manipulate data via functional `optimisticData`', async () => {
1074+
const key = createKey()
1075+
const renderedData = []
1076+
1077+
function useOptimisticDataMutate(_key, data, fallback) {
1078+
const { mutate } = useSWRConfig()
1079+
return () => {
1080+
return mutate(_key, createResponse(data, { delay: 20 }), {
1081+
optimisticData() {
1082+
return fallback
1083+
}
1084+
})
1085+
}
1086+
}
1087+
1088+
function Page() {
1089+
const mutateWithOptData = useOptimisticDataMutate(key, 'final', 'loading')
1090+
const { data } = useSWR(key)
1091+
renderedData.push(data)
1092+
return (
1093+
<div>
1094+
<button onClick={() => mutateWithOptData()}>mutate</button>
1095+
<div>data: {String(data)}</div>
1096+
</div>
1097+
)
1098+
}
1099+
1100+
renderWithConfig(<Page />)
1101+
fireEvent.click(screen.getByText('mutate'))
1102+
await sleep(30)
1103+
1104+
expect(renderedData).toEqual([undefined, 'loading', 'final'])
1105+
})
1106+
10411107
it('should rollback optimistic updates when mutation fails', async () => {
10421108
const key = createKey()
10431109
const renderedData = []
@@ -1152,7 +1218,7 @@ describe('useSWR - local mutation', () => {
11521218
const { data, mutate } = useSWR(key, () => 'foo')
11531219
mutatePage = () =>
11541220
mutate(new Promise(res => setTimeout(() => res('baz'), 20)), {
1155-
optimisticData: 'bar',
1221+
optimisticData: () => 'bar',
11561222
revalidate: false,
11571223
populateCache: v => '!' + v
11581224
})

0 commit comments

Comments
 (0)