@@ -693,17 +693,15 @@ describe('useQuery', () => {
693693 // required to make sure no additional renders are happening after data is successfully fetched for the second time
694694 await sleep ( 100 )
695695
696- expect ( states . length ) . toBe ( 5 )
696+ expect ( states . length ) . toBe ( 4 )
697697 // First load
698698 expect ( states [ 0 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
699699 // First success
700700 expect ( states [ 1 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
701701 // Remove
702702 expect ( states [ 2 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
703- // Hook state update
704- expect ( states [ 3 ] ) . toMatchObject ( { isPending : true , isSuccess : false } )
705703 // Second success
706- expect ( states [ 4 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
704+ expect ( states [ 3 ] ) . toMatchObject ( { isPending : false , isSuccess : true } )
707705 } )
708706
709707 it ( 'should fetch when refetchOnMount is false and nothing has been fetched yet' , async ( ) => {
@@ -1716,7 +1714,7 @@ describe('useQuery', () => {
17161714 act ( ( ) => rendered . rerender ( < Page count = { 2 } /> ) )
17171715 await waitFor ( ( ) => rendered . getByText ( 'error: Error test' ) )
17181716
1719- await waitFor ( ( ) => expect ( states . length ) . toBe ( 8 ) )
1717+ await waitFor ( ( ) => expect ( states . length ) . toBe ( 6 ) )
17201718 // Initial
17211719 expect ( states [ 0 ] ) . toMatchObject ( {
17221720 data : undefined ,
@@ -1741,46 +1739,30 @@ describe('useQuery', () => {
17411739 error : null ,
17421740 isPlaceholderData : true ,
17431741 } )
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- } )
17521742 // New data
1753- expect ( states [ 4 ] ) . toMatchObject ( {
1743+ expect ( states [ 3 ] ) . toMatchObject ( {
17541744 data : 1 ,
17551745 isFetching : false ,
17561746 status : 'success' ,
17571747 error : null ,
17581748 isPlaceholderData : false ,
17591749 } )
17601750 // 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 ( {
17701752 data : 1 ,
17711753 isFetching : true ,
17721754 status : 'success' ,
17731755 error : null ,
17741756 isPlaceholderData : true ,
17751757 } )
17761758 // Error
1777- expect ( states [ 7 ] ) . toMatchObject ( {
1759+ expect ( states [ 5 ] ) . toMatchObject ( {
17781760 data : undefined ,
17791761 isFetching : false ,
17801762 status : 'error' ,
17811763 isPlaceholderData : false ,
17821764 } )
1783- expect ( states [ 7 ] ? .error ) . toHaveProperty ( 'message' , 'Error test' )
1765+ expect ( states [ 5 ] ! . error ) . toHaveProperty ( 'message' , 'Error test' )
17841766 } )
17851767
17861768 it ( 'should not show initial data from next query if placeholderData is set' , async ( ) => {
@@ -1825,7 +1807,7 @@ describe('useQuery', () => {
18251807 rendered . getByText ( 'data: 1, count: 1, isFetching: false' ) ,
18261808 )
18271809
1828- expect ( states . length ) . toBe ( 5 )
1810+ expect ( states . length ) . toBe ( 4 )
18291811
18301812 // Initial
18311813 expect ( states [ 0 ] ) . toMatchObject ( {
@@ -1848,15 +1830,8 @@ describe('useQuery', () => {
18481830 isSuccess : true ,
18491831 isPlaceholderData : false ,
18501832 } )
1851- // Hook state update
1852- expect ( states [ 3 ] ) . toMatchObject ( {
1853- data : 99 ,
1854- isFetching : true ,
1855- isSuccess : true ,
1856- isPlaceholderData : false ,
1857- } )
18581833 // New data
1859- expect ( states [ 4 ] ) . toMatchObject ( {
1834+ expect ( states [ 3 ] ) . toMatchObject ( {
18601835 data : 1 ,
18611836 isFetching : false ,
18621837 isSuccess : true ,
@@ -5993,4 +5968,127 @@ describe('useQuery', () => {
59935968 await waitFor ( ( ) => rendered . getByText ( 'status: success' ) )
59945969 await waitFor ( ( ) => rendered . getByText ( 'data: 1' ) )
59955970 } )
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+ } )
59966094} )
0 commit comments