In RTK Query, which hooks does createApi generate for a getPosts query and an updatePost mutation, and how do isLoading and isFetching differ?
answer
- use + Endpointname + type
- a lazy variant for queries
- mutations return a tuple
- first load versus any request
basics
~20 sRTK Query's React createApi generates useGetPostsQuery and useLazyGetPostsQuery for the query and useUpdatePostMutation for the mutation. isLoading is true only while the hook has no data yet; isFetching is true whenever a request is in flight, including refetches.
solid answer
~40 sCreated from `@reduxjs/toolkit/query/react`, `createApi` names hooks `use` + capitalised endpoint name + `Query` or `Mutation`: `useGetPostsQuery(arg, options)` subscribes and fetches on mount; `useLazyGetPostsQuery()` returns `[trigger, result, lastPromiseInfo]` and fetches only when you call `trigger`; `useUpdatePostMutation()` returns `[updatePost, result]`. The same hooks live on `api.endpoints.getPosts.useQuery`. In a query result, `isLoading` means **in flight with no data yet** — the first load; `isFetching` means **any request in flight**, including a refetch or an argument change. Because `data` keeps the latest result regardless of the argument, switching from page 1 to page 2 shows `isLoading: false`, `isFetching: true` and page 1's `data`, while `currentData` is `undefined` until page 2 arrives.
code
ts · 28 linesimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export type Post = { id: number; title: string; body: string };
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Post'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
}),
getPost: build.query<Post, number>({
query: (id) => `posts/${id}`,
}),
updatePost: build.mutation<Post, Pick<Post, 'id'> & Partial<Post>>({
query: ({ id, ...patch }) => ({ url: `posts/${id}`, method: 'PATCH', body: patch }),
}),
}),
});
// Generated hooks: use + Endpointname + Query | Mutation
export const {
useGetPostsQuery,
useLazyGetPostsQuery,
useGetPostQuery,
useUpdatePostMutation,
} = api;go deeper
Recall the naming rule for generated hooks and the difference between isLoading and isFetching.
Explain data versus currentData on an argument change, lazy queries, skipToken and unwrap on mutations.
Design loading states that never blank the UI on refetch, and pick data or currentData per screen deliberately.
Set conventions for loading and error states across screens built on generated hooks so the product behaves consistently.
## What createApi generates RTK Query is the data-fetching layer shipped inside Redux Toolkit 2. You describe endpoints once with `createApi`, and the React build of it (imported from `@reduxjs/toolkit/query/react`) generates hooks for each one. The naming rule is fixed: > `use` + endpoint name with its first letter capitalised + `Query` | `Mutation` | `InfiniteQuery` | Endpoint definition | Generated on the api object | What it returns | |---|---|---| | `getPosts: build.query(...)` | `useGetPostsQuery` | Result object: `data`, `error`, `isLoading`, `isFetching`, `refetch`... | | same | `useLazyGetPostsQuery` | `[trigger, result, lastPromiseInfo]` | | `updatePost: build.mutation(...)` | `useUpdatePostMutation` | `[updatePost, result]` | The same hooks exist under generic names on the endpoint objects — `api.endpoints.getPosts.useQuery()` — together with a few that are generated only there, such as `useQueryState` and `useQuerySubscription`. ## Query hook versus lazy query hook - **`useGetPostsQuery(arg, options)`** starts a subscription when the component mounts and fetches unless the data is already cached. Pass `skip: true` or the `skipToken` value as the argument to hold it back. - **`useLazyGetPostsQuery()`** does nothing until you call `trigger(arg)`, which suits search-on-submit or a "load details" button. ## Mutation hook `const [updatePost, { isLoading, error }] = useUpdatePostMutation()` gives you a trigger and the state of the latest call. `updatePost(changes)` returns a promise-like object; `.unwrap()` on it resolves with the data or throws the error, which is the idiomatic way to `await` it in a handler. ## isLoading versus isFetching The generated query hooks expose derived booleans rather than one `status` string, because several can be true at once: - **`isUninitialized`** — the query has not started (for example, while skipped). - **`isLoading`** — a request is in flight **and the hook has no data yet**: the first load. - **`isFetching`** — **any** request is in flight: the first load, a refetch, a tag-triggered refetch, or a new argument. - **`isSuccess`** — there is data from a successful request. - **`isError`** — the latest request failed. So `isLoading` implies `isFetching`, but not the other way round. The documented use is a skeleton on `isLoading` and a dimmed list or small spinner on `isFetching`. ## data versus currentData on an argument change 1. The component renders `useGetPostsQuery(page)` with `page = 1`; page 1 loads. 2. The user clicks "next", so `page = 2`. 3. While page 2 loads: `isLoading` is `false` (the hook already has data), `isFetching` is `true`, `data` is still **page 1** — the latest result regardless of argument — and `currentData` is `undefined`, because it tracks only the current argument. Show `data` to keep the old page visible while the next loads; show `currentData` when stale content for a different argument would mislead. ## Common mistakes - **Showing a full-page spinner on `isFetching`**, so every background refetch blanks the screen. - **Expecting `useLazy...Query` to fetch on mount.** - **Hand-writing hook names**; the generated names must match the endpoint key exactly. - **Importing `createApi` from `@reduxjs/toolkit/query`** (the UI-agnostic entry point) and then looking for hooks: only the `/react` entry point generates them.
- In RTK Query, how do you stop useGetPostQuery(id) from running until an id is known?Pass `skipToken` as the argument — `useGetPostQuery(id ?? skipToken)` — or `{ skip: !id }` in the options. The hook stays `isUninitialized` and sends nothing; once a real argument arrives, it subscribes and fetches. `skipToken` also keeps the TypeScript argument type honest.
- How do you await the result of an RTK Query mutation in an event handler?Call `await updatePost(changes).unwrap()`. `unwrap()` resolves with the response data or throws the error, so a `try/catch` around it handles failure. Without `unwrap`, the returned promise resolves to an object with either `data` or `error` and never throws.
- When would you show currentData instead of data from an RTK Query hook?When showing the previous argument's result would mislead — for example, a detail panel that must never show user A's profile while user B's loads. `currentData` is `undefined` until the current argument's data arrives, while `data` keeps the latest result from any argument.
saying these in an interview costs you the question
- isLoading is true during every refetch
- useLazyGetPostsQuery fetches as soon as the component mounts
- Changing the hook argument clears data until the new response arrives
- The mutation hook returns an object with a mutate function
- createApi from '@reduxjs/toolkit/query' also generates React hooks