Skip to content

Commit

Permalink
fix: use the latest reference of fetcher with suspense mode (#1803)
Browse files Browse the repository at this point in the history
  • Loading branch information
koba04 committed Jan 26, 2022
1 parent fdd5c33 commit e3dc48a
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 7 deletions.
3 changes: 3 additions & 0 deletions src/use-swr.ts
Expand Up @@ -510,6 +510,9 @@ export const useSWRHandler = <Data = any, Error = any>(
// If there is no `error`, the `revalidation` promise needs to be thrown to
// the suspense boundary.
if (suspense && isUndefined(data) && key) {
// Always update fetcher and config refs even with the Suspense mode.
fetcherRef.current = fetcher
configRef.current = config
throw isUndefined(error) ? revalidate(WITH_DEDUPE) : error
}

Expand Down
74 changes: 72 additions & 2 deletions test/use-swr-fetcher.test.tsx
@@ -1,5 +1,5 @@
import { act, screen } from '@testing-library/react'
import React, { useState } from 'react'
import { act, fireEvent, screen } from '@testing-library/react'
import React, { Suspense, useState } from 'react'
import useSWR from 'swr'
import { createKey, renderWithConfig, nextTick } from './utils'

Expand Down Expand Up @@ -33,4 +33,74 @@ describe('useSWR - fetcher', () => {
// Should fetch with the new fetcher.
await screen.findByText('data:bar')
})

it('should use the latest fetcher reference when the key has been changed', async () => {
const key = createKey()
let fetcher = () => 'foo'

function Page() {
const [prefix, setPrefix] = useState('a')
const { data } = useSWR(prefix + key, fetcher)

return (
<div>
<p>data:{data}</p>
<button
onClick={() => {
setPrefix('b')
}}
>
mutate
</button>
</div>
)
}

renderWithConfig(<Page />)
await screen.findByText('data:foo')

// Change the fetcher and make sure the ref is updated.
fetcher = () => 'bar'
fireEvent.click(screen.getByText('mutate'))

// Should fetch with the new fetcher.
await screen.findByText('data:bar')
})

it('should use the latest fetcher reference with the suspense mode when the key has been changed', async () => {
const key = createKey()
let fetcher = () => 'foo'

function Page() {
const [prefix, setPrefix] = useState('a')
const { data } = useSWR(prefix + key, fetcher, { suspense: true })

return (
<div>
<p>data:{data}</p>
<button
onClick={() => {
setPrefix('b')
}}
>
mutate
</button>
</div>
)
}

renderWithConfig(
<Suspense fallback="loading">
<Page />
</Suspense>
)
await screen.findByText('data:foo')

// Change the fetcher and make sure the ref is updated.
fetcher = () => 'bar'
fireEvent.click(screen.getByText('mutate'))

// Should fetch with the new fetcher.
await screen.findByText('data:bar')
})
})
65 changes: 60 additions & 5 deletions test/use-swr-infinite.test.tsx
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { Suspense, useEffect, useState } from 'react'
import { fireEvent, act, screen } from '@testing-library/react'
import { mutate as globalMutate, useSWRConfig, SWRConfig } from 'swr'
import useSWRInfinite, { unstable_serialize } from 'swr/infinite'
Expand Down Expand Up @@ -166,7 +166,9 @@ describe('useSWRInfinite', () => {
}

// fetch with offset
return `/api/${key}?offset=${previousPageData[previousPageData.length - 1].id}`
return `/api/${key}?offset=${
previousPageData[previousPageData.length - 1].id
}`
},
mockAPIFetcher,
{
Expand Down Expand Up @@ -790,7 +792,11 @@ describe('useSWRInfinite', () => {
await screen.findByText('data:response data')

await act(() =>
mutateCustomCache(unstable_serialize(() => key), 'local-mutation', false)
mutateCustomCache(
unstable_serialize(() => key),
'local-mutation',
false
)
)
await screen.findByText('data:local-mutation')
})
Expand All @@ -799,7 +805,10 @@ describe('useSWRInfinite', () => {
const loggedValues = []

function Page() {
const { size, setSize } = useSWRInfinite(() => null, () => '')
const { size, setSize } = useSWRInfinite(
() => null,
() => ''
)
loggedValues.push(size)
return <button onClick={() => setSize(1)}>set size</button>
}
Expand Down Expand Up @@ -892,7 +901,11 @@ describe('useSWRInfinite', () => {
let v = 'old'

function Page() {
const { data, size, mutate: boundMutate } = useSWRInfinite(
const {
data,
size,
mutate: boundMutate
} = useSWRInfinite(
i => [key, i],
() => v
)
Expand Down Expand Up @@ -1095,4 +1108,46 @@ describe('useSWRInfinite', () => {
})
).toBeTruthy()
})

// https://github.com/vercel/swr/issues/1776
it('should update the getKey reference with the suspense mode', async () => {
const keyA = 'keyA' + createKey()
const keyB = 'keyB' + createKey()

const apiData = {
[keyA]: ['A1', 'A2', 'A3'],
[keyB]: ['B1', 'B2', 'B3']
}

function Page() {
const [status, setStatus] = useState('a')
const { data, setSize } = useSWRInfinite(
() => (status === 'a' ? keyA : keyB),
key => createResponse(apiData[key]),
{ suspense: true }
)
return (
<>
<div>data: {String(data)}</div>
<button
onClick={() => {
setStatus('b')
setSize(1)
}}
>
mutate
</button>
</>
)
}
renderWithConfig(
<Suspense fallback="loading">
<Page />
</Suspense>
)
await screen.findByText('data: A1,A2,A3')

fireEvent.click(screen.getByText('mutate'))
await screen.findByText('data: B1,B2,B3')
})
})

0 comments on commit e3dc48a

Please sign in to comment.