Skip to content

Commit 19445a0

Browse files
fix(query-core): infinite re-renders in useQueries (#9639)
* test(useQueries): add test for preventing infinite re-renders * fix(query-core): resolve infinite re-renders in queriesObserver * style(useQueries): remove unnecessary comments * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent ccedf33 commit 19445a0

2 files changed

Lines changed: 81 additions & 10 deletions

File tree

‎packages/query-core/src/queriesObserver.ts‎

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,29 +118,30 @@ export class QueriesObserver<
118118
observer.getCurrentResult(),
119119
)
120120

121+
consthasLengthChange=prevObservers.length!==newObservers.length
121122
consthasIndexChange=newObservers.some(
122123
(observer,index)=>observer!==prevObservers[index],
123124
)
125+
consthasStructuralChange=hasLengthChange||hasIndexChange
124126

125-
consthasResultChange=
126-
prevObservers.length===newObservers.length&&!hasIndexChange
127-
? newResult.some((result,index)=>{
128-
constprev=this.#result[index]
129-
return!prev||!shallowEqualObjects(result,prev)
130-
})
131-
: true
127+
consthasResultChange=hasStructuralChange
128+
? true
129+
: newResult.some((result,index)=>{
130+
constprev=this.#result[index]
131+
return!prev||!shallowEqualObjects(result,prev)
132+
})
132133

133-
if(!hasIndexChange&&!hasResultChange)return
134+
if(!hasStructuralChange&&!hasResultChange)return
134135

135-
if(hasIndexChange){
136+
if(hasStructuralChange){
136137
this.#observers =newObservers
137138
}
138139

139140
this.#result =newResult
140141

141142
if(!this.hasListeners())return
142143

143-
if(hasIndexChange){
144+
if(hasStructuralChange){
144145
difference(prevObservers,newObservers).forEach((observer)=>{
145146
observer.destroy()
146147
})

‎packages/react-query/src/__tests__/useQueries.test.tsx‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,4 +1741,74 @@ describe('useQueries', () => {
17411741
// still no extra calls to combine
17421742
expect(spy).toHaveBeenCalledTimes(3)
17431743
})
1744+
1745+
it('should not cause infinite re-renders when removing last query',async()=>{
1746+
letrenderCount=0
1747+
1748+
functionPage(){
1749+
const[queries,setQueries]=React.useState([
1750+
{
1751+
queryKey: ['query1'],
1752+
queryFn: ()=>'data1',
1753+
},
1754+
{
1755+
queryKey: ['query2'],
1756+
queryFn: ()=>'data2',
1757+
},
1758+
])
1759+
renderCount++
1760+
1761+
constresult=useQueries({ queries })
1762+
1763+
return(
1764+
<div>
1765+
<divdata-testid="render-count">renders: {renderCount}</div>
1766+
<divdata-testid="query-count">queries: {result.length}</div>
1767+
<button
1768+
onClick={()=>{
1769+
setQueries([
1770+
{
1771+
queryKey: ['query1'],
1772+
queryFn: ()=>'data1',
1773+
},
1774+
])
1775+
}}
1776+
>
1777+
remove last
1778+
</button>
1779+
<button
1780+
onClick={()=>{
1781+
setQueries([
1782+
{
1783+
queryKey: ['query2'],
1784+
queryFn: ()=>'data2',
1785+
},
1786+
])
1787+
}}
1788+
>
1789+
remove first
1790+
</button>
1791+
</div>
1792+
)
1793+
}
1794+
1795+
constrendered=renderWithClient(queryClient,<Page/>)
1796+
1797+
awaitvi.advanceTimersByTimeAsync(0)
1798+
renderCount=0
1799+
1800+
fireEvent.click(rendered.getByRole('button',{name: /removelast/i}))
1801+
awaitvi.advanceTimersByTimeAsync(100)
1802+
1803+
expect(renderCount).toBeLessThan(10)
1804+
expect(rendered.getByTestId('query-count').textContent).toBe('queries: 1')
1805+
1806+
renderCount=0
1807+
1808+
fireEvent.click(rendered.getByRole('button',{name: /removefirst/i}))
1809+
awaitvi.advanceTimersByTimeAsync(100)
1810+
1811+
expect(renderCount).toBeLessThan(10)
1812+
expect(rendered.getByTestId('query-count').textContent).toBe('queries: 1')
1813+
})
17441814
})

0 commit comments

Comments
 (0)