Skip to content

fetchNextPage always triggering onQueryStarted, even when queryFulfilled already occured. #5167

@JacobJaffe

Description

@JacobJaffe

It looks like the entire lifecycle of onQueryStarted occurs when fetchNextPage is called.

However, often fetchNextPage is expected to often indicate end-of-list.

This seems like unexpected behavior, because no additional base-query usage is made, but any side effects of onQueryStarted DO occur.

Take the following example, with the code below.

In this example, patchCachedUser syncs some metadata between the query-notifications endpoints with the get-user endpoint (the total notifications count, to prevent flickering while states reconcile).

The behavior that occurs is:

  1. Hook mounts with page of data. onQueryStarted does not fire unless this is the first time the hook is mounted, as expected.
  2. The end of list is reached, triggering fetchNextPage.
  3. fetchNextPage, via getNextPageParam, knows we're at the end-of-list, returning undefined.
  4. onQueryStarted is fired, ALWAYS. however, queryFulfilled is already resolved, and so it does the remaining work (e.g. pessimistic updates), using the old data.
 getNotifications: build.infiniteQuery<
      {
        notifications: Notification[];
        user: Pick<User, "numUnreadNotifications">
      },
      {
        uid: string;
        limit: number;
        filter: "read" | "unread"
      },
      PaginationCursor | undefined
    >({
      onQueryStarted: async (args, { dispatch, queryFulfilled }) => {
        try {
          console.log("Fetching notifications for user:"); // THIS logs during a `fetchNextPage` call
          const { data } = await queryFulfilled; // Base query has its own logging -- this is already resolved during the calls.
          const lastPage = data.pages[data.pages.length - 1];
          const user = lastPage.user;
          dispatch(patchCachedUser(args, user)); // This now badly assumes that the data is the most-recent from the API, but its from a previous cached call.
        } catch (error) {
          void error;
        }
      },
     infiniteQueryOptions: {
        initialPageParam: undefined,
        getNextPageParam: ({
          notifications: lastPage,
        }): undefined | PaginationCursor => {
          const lastItem = lastPage?.at(-1);
          return lastItem && { createdAt: lastItem.createdAt };
        },
      },

This looks like unexpected behavior to me, and I don't see a clean way to get around this (to prevent side affects from occuring multiple times from the same resolved underlying queryFulfilled)

Metadata

Metadata

Assignees

No one assigned

    Labels

    RTK-QueryIssues related to Redux-Toolkit-Query

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions