@@ -12,7 +12,9 @@ import ApolloClient, {
1212 WatchQueryOptions ,
1313} from 'apollo-client' ;
1414import { DocumentNode } from 'graphql' ;
15- import { useContext , useEffect , useMemo , useState } from 'react' ;
15+ import pick from 'lodash/pick' ;
16+ import { useContext , useEffect , useMemo , useRef , useState } from 'react' ;
17+
1618import { useApolloClient } from './ApolloContext' ;
1719import { SSRContext } from './internal/SSRContext' ;
1820import actHack from './internal/actHack' ;
@@ -123,32 +125,89 @@ export function useQuery<
123125 fetchResults ,
124126 ]
125127 ) ;
126- const observableQuery = useMemo (
127- ( ) =>
128- getCachedObservableQuery < TData , TVariables > ( client , watchQueryOptions ) ,
129- [ client , watchQueryOptions ]
128+
129+ const previousClient = useRef < ApolloClient < TCache > > ( client ) ;
130+ const previousQuery = useRef < DocumentNode > ( query ) ;
131+ const observableQuery = useRef < ObservableQuery < TData , TVariables > | null > (
132+ null
130133 ) ;
134+ const isObservableQueryInitialized = useRef < boolean > ( false ) ;
135+
136+ if ( previousClient . current !== client || previousQuery . current !== query ) {
137+ observableQuery . current = null ;
138+ isObservableQueryInitialized . current = false ;
139+
140+ if ( previousClient . current !== client ) {
141+ previousClient . current = client ;
142+ }
143+
144+ if ( previousQuery . current !== query ) {
145+ previousQuery . current = query ;
146+ }
147+ }
131148
132149 const [ responseId , setResponseId ] = useState ( 0 ) ;
133150
151+ const invalidateCurrentResult = ( ) => {
152+ // A hack to get rid React warnings during tests. The default
153+ // implementation of `actHack` just invokes the callback immediately.
154+ // In tests, it's replaced with `act` from react-testing-library.
155+ // A better solution welcome.
156+ actHack ( ( ) => {
157+ setResponseId ( x => x + 1 ) ;
158+ } ) ;
159+ } ;
160+
161+ let currentObservableQuery : ObservableQuery < TData , TVariables > ;
162+ if ( observableQuery . current ) {
163+ currentObservableQuery = observableQuery . current ;
164+ } else {
165+ currentObservableQuery = getCachedObservableQuery < TData , TVariables > (
166+ client ,
167+ watchQueryOptions
168+ ) ;
169+ observableQuery . current = currentObservableQuery ;
170+ }
171+
172+ useEffect (
173+ ( ) => {
174+ if ( isObservableQueryInitialized . current ) {
175+ currentObservableQuery . setOptions (
176+ pick ( watchQueryOptions , [
177+ 'errorPolicy' ,
178+ 'fetchPolicy' ,
179+ 'fetchResults' ,
180+ 'notifyOnNetworkStatusChange' ,
181+ 'pollInterval' ,
182+ 'variables' ,
183+ ] )
184+ ) ;
185+ invalidateCurrentResult ( ) ;
186+ } else {
187+ isObservableQueryInitialized . current = true ;
188+ }
189+ } ,
190+ [ currentObservableQuery , watchQueryOptions ]
191+ ) ;
192+
134193 const currentResult = useMemo < QueryHookResult < TData , TVariables > > (
135194 ( ) => {
136195 const helpers = {
137- fetchMore : observableQuery . fetchMore . bind ( observableQuery ) ,
138- refetch : observableQuery . refetch . bind ( observableQuery ) ,
139- startPolling : observableQuery . startPolling . bind ( observableQuery ) ,
140- stopPolling : observableQuery . stopPolling . bind ( observableQuery ) ,
141- updateQuery : observableQuery . updateQuery . bind ( observableQuery ) ,
196+ fetchMore : currentObservableQuery . fetchMore . bind ( observableQuery ) ,
197+ refetch : currentObservableQuery . refetch . bind ( observableQuery ) ,
198+ startPolling : currentObservableQuery . startPolling . bind ( observableQuery ) ,
199+ stopPolling : currentObservableQuery . stopPolling . bind ( observableQuery ) ,
200+ updateQuery : currentObservableQuery . updateQuery . bind ( observableQuery ) ,
142201 } ;
143202
144- const result = observableQuery . currentResult ( ) ;
203+ const result = currentObservableQuery . currentResult ( ) ;
145204
146205 // return the old result data when there is an error
147206 let data = result . data as TData ;
148207 if ( result . error || result . errors ) {
149208 data = {
150209 ...result . data ,
151- ...( observableQuery . getLastResult ( ) || { } ) . data ,
210+ ...( currentObservableQuery . getLastResult ( ) || { } ) . data ,
152211 } ;
153212 }
154213
@@ -179,7 +238,7 @@ export function useQuery<
179238 partial : result . partial ,
180239 } ;
181240 } ,
182- [ shouldSkip , responseId , observableQuery ]
241+ [ shouldSkip , responseId , currentObservableQuery ]
183242 ) ;
184243
185244 useEffect (
@@ -188,16 +247,7 @@ export function useQuery<
188247 return ;
189248 }
190249
191- const invalidateCurrentResult = ( ) => {
192- // A hack to get rid React warnings during tests. The default
193- // implementation of `actHack` just invokes the callback immediately.
194- // In tests, it's replaced with `act` from react-testing-library.
195- // A better solution welcome.
196- actHack ( ( ) => {
197- setResponseId ( x => x + 1 ) ;
198- } ) ;
199- } ;
200- const subscription = observableQuery . subscribe (
250+ const subscription = currentObservableQuery . subscribe (
201251 invalidateCurrentResult ,
202252 invalidateCurrentResult
203253 ) ;
@@ -217,11 +267,11 @@ export function useQuery<
217267 if ( suspend ) {
218268 // throw a promise - use the react suspense to wait until the data is
219269 // available
220- throw observableQuery . result ( ) ;
270+ throw currentObservableQuery . result ( ) ;
221271 }
222272
223273 if ( ssrInUse ) {
224- ssrManager ! . register ( observableQuery . result ( ) ) ;
274+ ssrManager ! . register ( currentObservableQuery . result ( ) ) ;
225275 }
226276 }
227277
0 commit comments