Skip to content

Commit 40650e8

Browse files
authored
fix mutate (#1075)
close #1070; close #1074;
1 parent 628ed71 commit 40650e8

File tree

2 files changed

+139
-6
lines changed

2 files changed

+139
-6
lines changed

src/use-swr.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ async function mutate<Data = any>(
112112

113113
// track timestamps before await asynchronously
114114
const beforeMutationTs = MUTATION_TS[key]
115-
const beforeConcurrentPromisesTs = CONCURRENT_PROMISES_TS[key]
116115

117116
let data: any, error: unknown
118117
let isAsyncMutation = false
@@ -142,10 +141,7 @@ async function mutate<Data = any>(
142141

143142
const shouldAbort = (): boolean | void => {
144143
// check if other mutations have occurred since we've started this mutation
145-
if (
146-
beforeMutationTs !== MUTATION_TS[key] ||
147-
beforeConcurrentPromisesTs !== CONCURRENT_PROMISES_TS[key]
148-
) {
144+
if (beforeMutationTs !== MUTATION_TS[key]) {
149145
if (error) throw error
150146
return true
151147
}
@@ -389,7 +385,7 @@ function useSWR<Data = any, Error = any>(
389385
// we have to ignore the revalidation result (res) because it's no longer fresh.
390386
// meanwhile, a new revalidation should be triggered when the mutation ends.
391387
if (
392-
MUTATION_TS[key] &&
388+
MUTATION_TS[key] !== undefined &&
393389
// case 1
394390
(startAt <= MUTATION_TS[key] ||
395391
// case 2

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

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,4 +506,141 @@ describe('useSWR - local mutation', () => {
506506
expect(mutationRecivedValues).toEqual([0, 1])
507507
expect(renderRecivedValues).toEqual([undefined, 0, 1, 2])
508508
})
509+
510+
it('async mutation case 1 (startAt <= MUTATION_TS[key])', async () => {
511+
let result = 0
512+
const fetcher = jest.fn(createResponse)
513+
function Component() {
514+
const { data, mutate: boundMutate } = useSWR(
515+
'mutate-7',
516+
() =>
517+
fetcher(0, {
518+
delay: 300
519+
}),
520+
{
521+
dedupingInterval: 200
522+
}
523+
)
524+
return (
525+
<div
526+
onClick={() => {
527+
boundMutate(async () => {
528+
result += 1
529+
return createResponse(result, {
530+
delay: 100
531+
})
532+
}, false)
533+
}}
534+
>
535+
{data !== undefined ? `data: ${data.toString()}` : 'loading'}
536+
</div>
537+
)
538+
}
539+
540+
render(<Component />)
541+
screen.getByText('loading')
542+
543+
await act(() => sleep(50))
544+
545+
fireEvent.click(screen.getByText('loading'))
546+
547+
await act(() => sleep(100))
548+
// mutate success
549+
await screen.findByText('data: 1')
550+
551+
await act(() => sleep(150))
552+
// fetcher result should be ignored
553+
expect(fetcher).toBeCalledTimes(1)
554+
await screen.findByText('data: 1')
555+
})
556+
557+
it('async mutation case 2 (startAt <= MUTATION_END_TS[key])', async () => {
558+
let result = 0
559+
const fetcher = jest.fn(createResponse)
560+
function Component() {
561+
const [key, setKey] = useState(null)
562+
const { data } = useSWR(
563+
key,
564+
() =>
565+
fetcher(0, {
566+
delay: 400
567+
}),
568+
{
569+
dedupingInterval: 200
570+
}
571+
)
572+
useEffect(() => {
573+
mutate(
574+
'mutate-8',
575+
async () => {
576+
result += 1
577+
return createResponse(result, {
578+
delay: 200
579+
})
580+
},
581+
false
582+
)
583+
setKey('mutate-8')
584+
}, [])
585+
return (
586+
<div>{data !== undefined ? `data: ${data.toString()}` : 'loading'}</div>
587+
)
588+
}
589+
590+
render(<Component />)
591+
screen.getByText('loading')
592+
593+
// mutate success
594+
await act(() => sleep(200))
595+
fireEvent.click(screen.getByText('data: 1'))
596+
597+
// fetcher result should be ignored
598+
await act(() => sleep(200))
599+
expect(fetcher).toBeCalledTimes(1)
600+
await screen.findByText('data: 1')
601+
})
602+
603+
it('async mutation case 3 (MUTATION_END_TS[key] === 0)', async () => {
604+
let result = 0
605+
const fetcher = jest.fn(createResponse)
606+
function Component() {
607+
const [key, setKey] = useState(null)
608+
const { data } = useSWR(
609+
key,
610+
() =>
611+
fetcher(0, {
612+
delay: 100
613+
}),
614+
{
615+
dedupingInterval: 200
616+
}
617+
)
618+
useEffect(() => {
619+
setKey('mutate-9')
620+
mutate(
621+
'mutate-9',
622+
async () => {
623+
result += 1
624+
return createResponse(result, { delay: 200 })
625+
},
626+
false
627+
)
628+
}, [])
629+
return (
630+
<div>{data !== undefined ? `data: ${data.toString()}` : 'loading'}</div>
631+
)
632+
}
633+
634+
render(<Component />)
635+
screen.getByText('loading')
636+
637+
// fetcher result should be ignored
638+
await act(() => sleep(100))
639+
expect(fetcher).toBeCalledTimes(1)
640+
screen.getByText('loading')
641+
642+
// mutate success
643+
await act(() => sleep(100))
644+
await screen.findByText('data: 1')
645+
})
509646
})

0 commit comments

Comments
 (0)