@@ -693,17 +693,15 @@ describe('useQuery', () => {
693
693
// required to make sure no additional renders are happening after data is successfully fetched for the second time
694
694
await sleep ( 100 )
695
695
696
- expect ( states . length ) . toBe ( 5 )
696
+ expect ( states . length ) . toBe ( 4 )
697
697
// First load
698
698
expect ( states [ 0 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
699
699
// First success
700
700
expect ( states [ 1 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
701
701
// Remove
702
702
expect ( states [ 2 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
703
- // Hook state update
704
- expect ( states [ 3 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
705
703
// Second success
706
- expect ( states [ 4 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
704
+ expect ( states [ 3 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
707
705
} )
708
706
709
707
it ( 'should fetch when refetchOnMount is false and nothing has been fetched yet' , async ( ) => {
@@ -1716,7 +1714,7 @@ describe('useQuery', () => {
1716
1714
act ( ( ) => rendered . rerender ( < Page count = { 2 } /> ) )
1717
1715
await waitFor ( ( ) => rendered . getByText ( 'error: Error test' ) )
1718
1716
1719
- await waitFor ( ( ) => expect ( states . length ) . toBe ( 8 ) )
1717
+ await waitFor ( ( ) => expect ( states . length ) . toBe ( 6 ) )
1720
1718
// Initial
1721
1719
expect ( states [ 0 ] ) . toMatchObject ( {
1722
1720
data : undefined ,
@@ -1741,46 +1739,30 @@ describe('useQuery', () => {
1741
1739
error : null ,
1742
1740
isPlaceholderData : true ,
1743
1741
} )
1744
- // Hook state update
1745
- expect ( states [ 3 ] ) . toMatchObject ( {
1746
- data : 0 ,
1747
- isFetching : true ,
1748
- status : 'success' ,
1749
- error : null ,
1750
- isPlaceholderData : true ,
1751
- } )
1752
1742
// New data
1753
- expect ( states [ 4 ] ) . toMatchObject ( {
1743
+ expect ( states [ 3 ] ) . toMatchObject ( {
1754
1744
data : 1 ,
1755
1745
isFetching : false ,
1756
1746
status : 'success' ,
1757
1747
error : null ,
1758
1748
isPlaceholderData : false ,
1759
1749
} )
1760
1750
// rerender Page 2
1761
- expect ( states [ 5 ] ) . toMatchObject ( {
1762
- data : 1 ,
1763
- isFetching : true ,
1764
- status : 'success' ,
1765
- error : null ,
1766
- isPlaceholderData : true ,
1767
- } )
1768
- // Hook state update again
1769
- expect ( states [ 6 ] ) . toMatchObject ( {
1751
+ expect ( states [ 4 ] ) . toMatchObject ( {
1770
1752
data : 1 ,
1771
1753
isFetching : true ,
1772
1754
status : 'success' ,
1773
1755
error : null ,
1774
1756
isPlaceholderData : true ,
1775
1757
} )
1776
1758
// Error
1777
- expect ( states [ 7 ] ) . toMatchObject ( {
1759
+ expect ( states [ 5 ] ) . toMatchObject ( {
1778
1760
data : undefined ,
1779
1761
isFetching : false ,
1780
1762
status : 'error' ,
1781
1763
isPlaceholderData : false ,
1782
1764
} )
1783
- expect ( states [ 7 ] ? .error ) . toHaveProperty ( 'message' , 'Error test' )
1765
+ expect ( states [ 5 ] ! . error ) . toHaveProperty ( 'message' , 'Error test' )
1784
1766
} )
1785
1767
1786
1768
it ( 'should not show initial data from next query if placeholderData is set' , async ( ) => {
@@ -1825,7 +1807,7 @@ describe('useQuery', () => {
1825
1807
rendered . getByText ( 'data: 1, count: 1, isFetching: false' ) ,
1826
1808
)
1827
1809
1828
- expect ( states . length ) . toBe ( 5 )
1810
+ expect ( states . length ) . toBe ( 4 )
1829
1811
1830
1812
// Initial
1831
1813
expect ( states [ 0 ] ) . toMatchObject ( {
@@ -1848,15 +1830,8 @@ describe('useQuery', () => {
1848
1830
isSuccess : true ,
1849
1831
isPlaceholderData : false ,
1850
1832
} )
1851
- // Hook state update
1852
- expect ( states [ 3 ] ) . toMatchObject ( {
1853
- data : 99 ,
1854
- isFetching : true ,
1855
- isSuccess : true ,
1856
- isPlaceholderData : false ,
1857
- } )
1858
1833
// New data
1859
- expect ( states [ 4 ] ) . toMatchObject ( {
1834
+ expect ( states [ 3 ] ) . toMatchObject ( {
1860
1835
data : 1 ,
1861
1836
isFetching : false ,
1862
1837
isSuccess : true ,
@@ -5993,4 +5968,127 @@ describe('useQuery', () => {
5993
5968
await waitFor ( ( ) => rendered . getByText ( 'status: success' ) )
5994
5969
await waitFor ( ( ) => rendered . getByText ( 'data: 1' ) )
5995
5970
} )
5971
+ it ( 'should reuse same data object reference when queryKey changes back to some cached data' , async ( ) => {
5972
+ const key = queryKey ( )
5973
+ const spy = vi . fn ( )
5974
+
5975
+ async function fetchNumber ( id : number ) {
5976
+ await sleep ( 5 )
5977
+ return { numbers : { current : { id } } }
5978
+ }
5979
+ function Test ( ) {
5980
+ const [ id , setId ] = React . useState ( 1 )
5981
+
5982
+ const { data } = useQuery ( {
5983
+ select : selector ,
5984
+ queryKey : [ key , 'user' , id ] ,
5985
+ queryFn : ( ) => fetchNumber ( id ) ,
5986
+ } )
5987
+
5988
+ React . useEffect ( ( ) => {
5989
+ spy ( data )
5990
+ } , [ data ] )
5991
+
5992
+ return (
5993
+ < div >
5994
+ < button name = "1" onClick = { ( ) => setId ( 1 ) } >
5995
+ 1
5996
+ </ button >
5997
+ < button name = "2" onClick = { ( ) => setId ( 2 ) } >
5998
+ 2
5999
+ </ button >
6000
+ < span > Rendered Id: { data ?. id } </ span >
6001
+ </ div >
6002
+ )
6003
+ }
6004
+
6005
+ function selector ( data : any ) {
6006
+ return data . numbers . current
6007
+ }
6008
+
6009
+ const rendered = renderWithClient ( queryClient , < Test /> )
6010
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6011
+
6012
+ spy . mockClear ( )
6013
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 1' ) )
6014
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6015
+
6016
+ spy . mockClear ( )
6017
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 2 / } ) )
6018
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 2' ) )
6019
+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) // called with undefined because id changed
6020
+
6021
+ spy . mockClear ( )
6022
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 1 / } ) )
6023
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 1' ) )
6024
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6025
+
6026
+ spy . mockClear ( )
6027
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 2 / } ) )
6028
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 2' ) )
6029
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6030
+ } )
6031
+ it ( 'should reuse same data object reference when queryKey changes and placeholderData is present' , async ( ) => {
6032
+ const key = queryKey ( )
6033
+ const spy = vi . fn ( )
6034
+
6035
+ async function fetchNumber ( id : number ) {
6036
+ await sleep ( 5 )
6037
+ return { numbers : { current : { id } } }
6038
+ }
6039
+ function Test ( ) {
6040
+ const [ id , setId ] = React . useState ( 1 )
6041
+
6042
+ const { data } = useQuery ( {
6043
+ select : selector ,
6044
+ queryKey : [ key , 'user' , id ] ,
6045
+ queryFn : ( ) => fetchNumber ( id ) ,
6046
+ placeholderData : { numbers : { current : { id : 99 } } } ,
6047
+ } )
6048
+
6049
+ React . useEffect ( ( ) => {
6050
+ spy ( data )
6051
+ } , [ data ] )
6052
+
6053
+ return (
6054
+ < div >
6055
+ < button name = "1" onClick = { ( ) => setId ( 1 ) } >
6056
+ 1
6057
+ </ button >
6058
+ < button name = "2" onClick = { ( ) => setId ( 2 ) } >
6059
+ 2
6060
+ </ button >
6061
+ < span > Rendered Id: { data ?. id } </ span >
6062
+ </ div >
6063
+ )
6064
+ }
6065
+
6066
+ function selector ( data : any ) {
6067
+ return data . numbers . current
6068
+ }
6069
+
6070
+ const rendered = renderWithClient ( queryClient , < Test /> )
6071
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6072
+
6073
+ spy . mockClear ( )
6074
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 99' ) )
6075
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 1' ) )
6076
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6077
+
6078
+ spy . mockClear ( )
6079
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 2 / } ) )
6080
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 99' ) )
6081
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 2' ) )
6082
+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) // called with undefined because id changed
6083
+
6084
+ spy . mockClear ( )
6085
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 1 / } ) )
6086
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 1' ) )
6087
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6088
+
6089
+ spy . mockClear ( )
6090
+ fireEvent . click ( rendered . getByRole ( 'button' , { name : / 2 / } ) )
6091
+ await waitFor ( ( ) => rendered . getByText ( 'Rendered Id: 2' ) )
6092
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6093
+ } )
5996
6094
} )
0 commit comments