Skip to content

Commit

Permalink
try fix cannot stop polling by setting refreshInterval (#632) (#638)
Browse files Browse the repository at this point in the history
* fix Cannot stop polling by setting refreshInterval (#632)

* clear exiting the effect when key changes

* add test case for a-b-a and try fix a-b-a

* set less task
  • Loading branch information
promer94 committed Sep 15, 2020
1 parent 894e5e4 commit b21ca4f
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 18 deletions.
16 changes: 8 additions & 8 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ function useSWR<Data = any, Error = any>(
let shouldUpdateState = false
for (let k in payload) {
if (stateRef.current[k] === payload[k]) {
continue;
continue
}

stateRef.current[k] = payload[k]
Expand Down Expand Up @@ -584,26 +584,26 @@ function useSWR<Data = any, Error = any>(
}
}, [key, revalidate])

// set up polling
useIsomorphicLayoutEffect(() => {
let timer = null
const tick = async () => {
if (
!stateRef.current.error &&
(config.refreshWhenHidden || isDocumentVisible()) &&
(config.refreshWhenOffline || isOnline())
(configRef.current.refreshWhenHidden || isDocumentVisible()) &&
(configRef.current.refreshWhenOffline || isOnline())
) {
// only revalidate when the page is visible
// if API request errored, we stop polling in this round
// and let the error retry function handle it
await revalidate({ dedupe: true })
}
if (config.refreshInterval) {
timer = setTimeout(tick, config.refreshInterval)
// Read the latest refreshInterval
if (configRef.current.refreshInterval && !stateRef.current.error) {
timer = setTimeout(tick, configRef.current.refreshInterval)
}
}
if (config.refreshInterval) {
timer = setTimeout(tick, config.refreshInterval)
if (configRef.current.refreshInterval) {
timer = setTimeout(tick, configRef.current.refreshInterval)
}
return () => {
if (timer) clearTimeout(timer)
Expand Down
120 changes: 110 additions & 10 deletions test/use-swr.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,110 @@ describe('useSWR - refresh', () => {
expect(container.firstChild.textContent).toMatchInlineSnapshot(`"count: 5"`)
})

it('should update data upon interval changes -- changes happened during revalidate', async () => {
let count = 0
const STOP_POLLING_THRESHOLD = 2
function Page() {
const [flag, setFlag] = useState(0)
const shouldPoll = flag < STOP_POLLING_THRESHOLD
const { data } = useSWR(
'/interval-changes-during-revalidate',
() => count++,
{
refreshInterval: shouldPoll ? 200 : 0,
dedupingInterval: 100,
onSuccess() {
setFlag(value => value + 1)
}
}
)
return (
<div onClick={() => setFlag(0)}>
count: {data} {flag}
</div>
)
}
const { container } = render(<Page />)
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 0"`
)

await waitForDomChange({ container }) // mount
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 0 1"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 1 2"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 1 2"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 1 2"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 1 2"`
)

await act(() => {
fireEvent.click(container.firstElementChild)
// it will setup a new 200ms timer
return new Promise(res => setTimeout(res, 100))
})

expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 1 0"`
)

await act(() => {
return new Promise(res => setTimeout(res, 100))
})

expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 2 1"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})

expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 3 2"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})

expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 3 2"`
)

await act(() => {
return new Promise(res => setTimeout(res, 200))
})

expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"count: 3 2"`
)
})

it('should allow use custom isEqual method', async () => {
function Page() {
const { data, revalidate } = useSWR(
Expand Down Expand Up @@ -1920,9 +2024,9 @@ describe('useSWR - key', () => {
})

it('should revalidate if a function key changes identity', async () => {
const closureFunctions: {[key: string]: () => Promise<string>} = {}
const closureFunctions: { [key: string]: () => Promise<string> } = {}

const closureFactory = (id) => {
const closureFactory = id => {
if (closureFunctions[id]) return closureFunctions[id]
closureFunctions[id] = () => Promise.resolve(`data-${id}`)
return closureFunctions[id]
Expand All @@ -1938,24 +2042,20 @@ describe('useSWR - key', () => {
const fnWithClosure = closureFactory(id)
const { data } = useSWR([fnWithClosure], fetcher)

return (
<div>
{data}
</div>
)
return <div>{data}</div>
}

const { container } = render(<Page />);
const { container } = render(<Page />)
const closureSpy = jest.spyOn(closureFunctions, 'first')
await waitForDomChange({ container })
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"data-first"`
)
expect(closureSpy).toHaveBeenCalledTimes(1)

// update, but don't change the id.
// Function identity should stay the same, and useSWR should not call the function again.
await act(() => updateId('first'));
await act(() => updateId('first'))
expect(container.firstChild.textContent).toMatchInlineSnapshot(
`"data-first"`
)
Expand Down

0 comments on commit b21ca4f

Please sign in to comment.