Skip to content

Commit 1146d52

Browse files
authored
fix: If newData is deeply to the latest state, broadcast the latest state (#1697)
1 parent ba110c3 commit 1146d52

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/use-swr.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ export const useSWRHandler = <Data = any, Error = any>(
263263
// For local state, compare and assign.
264264
if (!compare(stateRef.current.data, newData)) {
265265
newState.data = newData
266+
} else {
267+
// data and newData are deeply equal
268+
// it should be safe to broadcast the stale data
269+
newState.data = stateRef.current.data
270+
// At the end of this function, `brocastState` invokes the `onStateUpdate` function,
271+
// which takes care of avoiding the re-render
266272
}
267273

268274
// For global state, it's possible that the key has changed.

test/use-swr-loading.test.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { act, screen, fireEvent } from '@testing-library/react'
2-
import React from 'react'
2+
import React, { useEffect } from 'react'
33
import useSWR from 'swr'
44
import {
55
createResponse,
@@ -77,6 +77,51 @@ describe('useSWR - loading', () => {
7777
expect(dataLoaded).toEqual(true)
7878
})
7979

80+
it('should avoid extra rerenders is the data is the `same`', async () => {
81+
let renderCount = 0,
82+
initialDataLoaded = false,
83+
mutationDataLoaded = false
84+
85+
const key = createKey()
86+
function Page() {
87+
const { data, mutate } = useSWR(
88+
key,
89+
async () => {
90+
const res = await createResponse({ greeting: 'hello' })
91+
initialDataLoaded = true
92+
return res
93+
},
94+
{ fallbackData: { greeting: 'hello' } }
95+
)
96+
97+
useEffect(() => {
98+
const timeout = setTimeout(
99+
() =>
100+
mutate(async () => {
101+
const res = await createResponse({ greeting: 'hello' })
102+
mutationDataLoaded = true
103+
return res
104+
}),
105+
200
106+
)
107+
108+
return () => clearTimeout(timeout)
109+
}, [mutate])
110+
111+
renderCount++
112+
return <div>{data?.greeting}</div>
113+
}
114+
115+
renderWithConfig(<Page />)
116+
screen.getByText('hello')
117+
118+
await act(() => sleep(1000)) // wait
119+
// it doesn't re-render, but fetch was triggered
120+
expect(initialDataLoaded).toEqual(true)
121+
expect(mutationDataLoaded).toEqual(true)
122+
expect(renderCount).toEqual(1)
123+
})
124+
80125
it('should return enumerable object', async () => {
81126
// If the returned object is enumerable, we can use the spread operator
82127
// to deconstruct all the keys.

0 commit comments

Comments
 (0)