Repository: tannerlinsley/react-query
File: packages/query-core/src/queryObserver.ts (TypeScript)

Code before:
): boolean {
  return (
    shouldLoadOnMount(query, options) ||
    (query.state.data !== undefined &&
      shouldFetchOn(query, options, options.refetchOnMount))
  )
}

function shouldFetchOn(
  query: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
  field: (typeof options)['refetchOnMount'] &
    (typeof options)['refetchOnWindowFocus'] &
    (typeof options)['refetchOnReconnect'],
) {
  if (
    resolveEnabled(options.enabled, query) !== false &&
    resolveStaleTime(options.staleTime, query) !== 'static'
  ) {
    const value = typeof field === 'function' ? field(query) : field

    return (
      value === 'always' ||
      (value !== false &&
        (isStale(query, options) || query.state.status === 'error'))
    )
  }
  return false
}

function shouldFetchOptionally(
  query: Query<any, any, any, any>,
  prevQuery: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
  prevOptions: QueryObserverOptions<any, any, any, any, any>,
): boolean {
  return (
    (query !== prevQuery ||
      resolveEnabled(prevOptions.enabled, query) === false) &&
    (!options.suspense || query.state.status !== 'error') &&
    isStale(query, options)
  )
}

function isStale(
  query: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
): boolean {
  return (
    resolveEnabled(options.enabled, query) !== false &&
    query.isStaleByTime(resolveStaleTime(options.staleTime, query))

Review comment (suggestion):
I think we can make the argument that if a query errors and has data, that this data should be considered stale. However, this check here doesn’t convey that and likely misses a couple of cases, e.g. `staleTime: 'static'`, which definitely shouldn't be refetched.

We do however have the option to mark a query as `invalidated`, and then the functions `isStale` and `isStaleByTime` on a query both check that in the correct order.

So maybe the right fix would be to just set `isInvalidated` in the reducer when we get an error:

https://github.com/TanStack/query/blob/1638c028df55648995d04431179904371a189772/packages/query-core/src/query.ts#L650

I think setting it to:

```
// flag existing data as invalidated if we get a background error
isInvalidated: true,
```

should suffice because if we get an error and have no data yet, we’re always implicitly stale

Code after:
): boolean {
  return (
    shouldLoadOnMount(query, options) ||
    (query.state.data !== undefined &&
      shouldFetchOn(query, options, options.refetchOnMount))
  )
}

function shouldFetchOn(
  query: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
  field: (typeof options)['refetchOnMount'] &
    (typeof options)['refetchOnWindowFocus'] &
    (typeof options)['refetchOnReconnect'],
) {
  if (
    resolveEnabled(options.enabled, query) !== false &&
    resolveStaleTime(options.staleTime, query) !== 'static'
  ) {
    const value = typeof field === 'function' ? field(query) : field

    return value === 'always' || (value !== false && isStale(query, options))
  }
  return false
}

function shouldFetchOptionally(
  query: Query<any, any, any, any>,
  prevQuery: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
  prevOptions: QueryObserverOptions<any, any, any, any, any>,
): boolean {
  return (
    (query !== prevQuery ||
      resolveEnabled(prevOptions.enabled, query) === false) &&
    (!options.suspense || query.state.status !== 'error') &&
    isStale(query, options)
  )
}

function isStale(
  query: Query<any, any, any, any>,
  options: QueryObserverOptions<any, any, any, any, any>,
): boolean {
  return (
    resolveEnabled(options.enabled, query) !== false &&
    query.isStaleByTime(resolveStaleTime(options.staleTime, query))
  )
}

// this function would decide if we will update the observer's 'current'