ForgeFrontend — Prepare, Practice, Crack
Secure checkout
Lifetime access
Instant PDF download
Free updates forever

Prepare · Practice · Crack

React Query vs Redux

Short answer

They are not alternatives. React Query manages server state — data that lives in a database and arrives over the network, with caching, refetching and invalidation handled for you. Redux manages client state your app owns outright. Most apps that felt they needed Redux were really storing server data in it.

React QueryRedux (Toolkit)
ManagesServer state — data you fetchedClient state — data you own
Source of truthYour backendThe store in the browser
CachingBuilt in, per query keyYou write it
Refetch on focus / reconnectBuilt inYou write it
Loading & error statesReturned by the hookYou track them yourself
Deduplicates parallel requestsYesNo
Boilerplate for one endpointOne hookSlice, thunk, three action types
Good forLists, details, mutationsCart, wizard, filters, auth UI
Different problems, which is why the comparison feels off until you name them.

The distinction that settles the question

Almost all confusion here comes from treating "state" as one thing. It is two. Client state is data your application invents and owns: whether a modal is open, what is in the cart, which tab is selected, what the user has typed. It exists only in the browser, you are the only writer, and it is correct by definition.

Server state is different in every important way. It lives in a database you do not control, other people can change it while you are looking at it, it arrives asynchronously and can fail, and the copy in your browser is a cache that starts going stale the moment it arrives. Treating that like client state — storing it in Redux and hoping — is the source of most of the complexity people blame on Redux.

Client stateServer state
Is the sidebar openThe list of orders
Current step of a checkout wizardThe logged-in user's profile
Selected filters and sort orderSearch results for those filters
Draft text in an unsaved formThe saved version of that record
Theme, locale, feature togglesProduct catalogue and prices
Sort your app's data into these two columns and the choice makes itself.

What fetching in Redux actually costs

The classic pattern is a thunk that dispatches pending, fulfilled and rejected actions into a slice that tracks data, loading and error. It works, everyone has written it, and it is worth seeing next to the alternative — not because the code is bad, but because of everything it does not do.

One endpoint, the Redux Toolkit wayjavascript
1const fetchOrders = createAsyncThunk("orders/fetch", async (userId) => {
2  const res = await fetch(`/api/orders?user=${userId}`);
3  if (!res.ok) throw new Error("Failed");
4  return res.json();
5});
6
7const ordersSlice = createSlice({
8  name: "orders",
9  initialState: { data: [], status: "idle", error: null },
10  reducers: {},
11  extraReducers: (b) => {
12    b.addCase(fetchOrders.pending,   (s) => { s.status = "loading"; })
13     .addCase(fetchOrders.fulfilled, (s, a) => { s.status = "ok"; s.data = a.payload; })
14     .addCase(fetchOrders.rejected,  (s, a) => { s.status = "error"; s.error = a.error.message; });
15  },
16});
17
18function Orders({ userId }) {
19  const dispatch = useDispatch();
20  const { data, status } = useSelector((s) => s.orders);
21  useEffect(() => { dispatch(fetchOrders(userId)); }, [dispatch, userId]);
22  // → works, but: no caching, no dedupe, no refetch on focus,
23  //   stale data from the previous userId shows during the new load
24}

Now count what is missing. Two components mounting at once fire two identical requests. Navigating away and back refetches from scratch even though the data arrived four seconds ago. Switching `userId` shows the previous user's orders while the new ones load. Nothing retries a failed request. Nothing knows the data has gone stale. Every one of those is a real ticket someone eventually files.

The same thing with React Queryjsx
1function Orders({ userId }) {
2  const { data, isPending, error } = useQuery({
3    queryKey: ["orders", userId],
4    queryFn: () => fetch(`/api/orders?user=${userId}`).then(r => r.json()),
5  });
6
7  if (isPending) return <Spinner />;
8  if (error) return <Error message={error.message} />;
9  return <List items={data} />;
10}
11// → caching per userId, dedupe across components, retry on failure,
12//   refetch on window focus and reconnect, and no stale cross-user data.
13//   The slice, the thunk and the three action types are all gone.

The features you would have to build yourself

It is easy to look at the two snippets and conclude React Query is a syntax improvement. It is not. The difference is a set of behaviours that are individually simple and collectively a large amount of code you would otherwise own.

BehaviourWhat it meansIn Redux you would…
Caching by keySame key, same cached dataWrite a normalised cache
DeduplicationTen components, one requestTrack in-flight promises
Stale-while-revalidateShow cached, refetch quietlyHand-roll timestamps
Refetch on focusData refreshes when you returnAdd window listeners
Retry with backoffTransient failures self-healWrite retry logic
Pagination / infinitePages kept, not refetchedManage page state manually
Optimistic updatesUI updates before the server repliesWrite rollback logic
Garbage collectionUnused cache is droppedNothing — memory grows
What comes free, and what it replaces.

The refetch-on-focus behaviour is the one people notice first in real use. A user switches to another tab, comes back three minutes later, and the numbers are current without a reload. Implementing that once is easy; implementing it for every query with the right staleness rules is not.

This is 1 of 75+ questions in the React Interview Kit

Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.

⚡ Get the React Interview Kit → ₹399

What Redux is still genuinely good at

None of this means Redux is finished. Once the server data moves out, what remains is client state — and for complex client state Redux is still an excellent tool, with a devtools experience nothing else matches. The point is that the remaining slice is much smaller than most apps assume.

The kind of state that belongs in a storejavascript
1const cartSlice = createSlice({
2  name: "cart",
3  initialState: { lines: [], coupon: null },
4  reducers: {
5    add(state, { payload }) {
6      const line = state.lines.find(l => l.sku === payload.sku);
7      line ? line.qty += payload.qty : state.lines.push(payload);
8    },
9    applyCoupon(state, { payload }) { state.coupon = payload; },
10    clear(state) { state.lines = []; state.coupon = null; },
11  },
12});
13// → owned entirely by the client, several components read it,
14//   the transitions are worth naming, and time-travel debugging
15//   genuinely helps when a discount comes out wrong.

The signals that a value belongs in a store are consistent: many unrelated components read it, the transitions are complex enough to deserve names, and you would benefit from seeing the sequence of changes when something goes wrong. A cart, a multi-step form, a canvas editor's undo stack and a permissions model all qualify. A list of products fetched from an API does not.

RTK Query: Redux's own answer

If your app already uses Redux Toolkit, RTK Query gives you most of React Query's behaviour inside the store you already have. It generates hooks from an endpoint definition, caches by cache key, deduplicates, and handles invalidation through a tag system. Choosing it over React Query is usually about what is already in the project rather than capability.

The same endpoint again, in RTK Queryjavascript
1export const api = createApi({
2  baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
3  tagTypes: ["Order"],
4  endpoints: (build) => ({
5    getOrders: build.query({
6      query: (userId) => `orders?user=${userId}`,
7      providesTags: ["Order"],
8    }),
9    cancelOrder: build.mutation({
10      query: (id) => ({ url: `orders/${id}/cancel`, method: "POST" }),
11      invalidatesTags: ["Order"],   // → the list refetches automatically
12    }),
13  }),
14});
15
16const { data, isLoading } = api.useGetOrdersQuery(userId);
SituationUse
Already on Redux ToolkitRTK Query — no second dependency
No store, or want to remove oneReact Query
Want the smallest possible librarySWR
Next.js App Router, mostly server dataServer Components + fetch; add React Query for interactive parts
GraphQLApollo or urql — they cache at the field level
Complex client-only stateRedux Toolkit, Zustand or Jotai
Choosing between the three, in practice.

Do you still need Redux at all?

For many applications, honestly, no — and saying so with reasons is a better interview answer than defending it. The prop-drilling problem that made Redux essential in 2016 is largely handled by context, server state has moved to query libraries, and where a store is genuinely needed the lighter options are often a better fit for a small team.

Redux still wins on three things worth naming: the devtools, including time-travel debugging that no alternative matches; the middleware ecosystem, when you need to intercept every action for logging, analytics or persistence; and predictability at scale, where explicit named actions make a large codebase easier to reason about than scattered setters.

Using both together

The two coexist cleanly once the boundary is clear, and this is what most real migrations end up looking like. React Query owns everything that came from the network. Redux owns what the client invented. Neither copies the other's data, and the point where they meet is usually a mutation that also touches local state.

A checkout that uses both, correctlyjsx
1function Checkout() {
2  const lines = useSelector(s => s.cart.lines);          // client state
3  const dispatch = useDispatch();
4
5  const { data: address } = useQuery({                    // server state
6    queryKey: ["address", "default"],
7    queryFn: getDefaultAddress,
8  });
9
10  const placeOrder = useMutation({
11    mutationFn: () => postOrder({ lines, addressId: address.id }),
12    onSuccess: (order) => {
13      dispatch(cartSlice.actions.clear());                // client state
14      queryClient.invalidateQueries({ queryKey: ["orders"] });  // server state
15      // → the orders list refetches; the cart empties. No duplicated data.
16    },
17  });
18
19  return <button onClick={() => placeOrder.mutate()}>Pay ₹{total(lines)}</button>;
20}

What is the difference between React Query and SWR?

Both do stale-while-revalidate caching for server state. SWR is smaller and more opinionated; React Query has a larger feature surface — mutations with optimistic updates, infinite queries, a query cancellation model and much stronger devtools. For a simple read-heavy app SWR is plenty; for anything with substantial mutations React Query usually pays off.

Does React Query replace useEffect for data fetching?

Yes, and that is one of its main benefits. The useEffect-plus-useState fetch pattern has to handle race conditions, cleanup on unmount, refetch on parameter changes and error states by hand, and most hand-written versions get at least one of those wrong. A query hook handles all four.

How does React Query know when to refetch?

Data becomes stale after staleTime, which defaults to zero. Once stale, it refetches on window focus, on network reconnect, on component mount and when the query key changes. Every one of those triggers is configurable per query or globally, and setting a sensible staleTime is the first tuning most apps do.

Do I need React Query with Next.js Server Components?

Not for data you can fetch on the server and render once — an async Server Component with fetch is simpler and ships no client JavaScript. You still want it for anything interactive: data that refetches on focus, infinite lists, optimistic mutations. The two compose, with the server render providing the initial cached data.

Is Zustand a replacement for Redux?

For most client state, yes. It gives you a store with far less ceremony, no provider requirement and a much smaller bundle. What you give up is Redux's devtools depth and its middleware ecosystem. For a team that only ever used Redux to avoid prop drilling, Zustand is usually the better trade.

Frequently asked questions

What is the difference between React Query and Redux?
React Query manages server state — data fetched over the network, with caching, staleness, refetching and retries handled for you. Redux is a general client state container for data your app owns outright. They solve different problems, which is why most apps that use both keep server data out of the store entirely.
Can React Query replace Redux?
It replaces the part of Redux that was storing fetched data, which in many applications is most of the store. What remains — a cart, a wizard, filters, UI state — may be small enough for useState and context. If it is genuinely complex, keep a store for it.
Should I use React Query or Redux for API calls?
React Query, or RTK Query if you are already on Redux Toolkit. Writing API calls as thunks means implementing caching, deduplication, retries and refetching yourself, and most hand-written versions cover only some of them.
What is the difference between server state and client state?
Client state is owned by your app and is correct by definition: a modal being open, a form draft, the selected tab. Server state is a cached copy of data that lives elsewhere, can be changed by other people, arrives asynchronously and goes stale. Only server state needs caching and revalidation.
What is the difference between React Query and RTK Query?
They cover the same ground. RTK Query lives inside a Redux store and defines endpoints in one API slice with a tag-based invalidation system; React Query is standalone and organises everything around query keys. If you already use Redux Toolkit, RTK Query avoids a second dependency.
Is Redux still worth learning in 2026?
Yes, because a large share of existing production React runs on it and interviews still ask. What has changed is whether you would add it to a new project — usually not, unless you have complex client state that benefits from named actions, middleware and time-travel debugging.
Can I use React Query and Redux together?
Yes, and it is a common and clean arrangement. React Query owns everything fetched, Redux owns everything the client invented, and neither copies the other's data. They meet at mutations, where a successful write clears local state and invalidates the relevant queries.
Does React Query store data in Redux?
No. It has its own in-memory cache keyed by query key, with its own staleness and garbage-collection rules. That independence is the point: the cache knows things — how old the data is, whether a refetch is in flight — that a plain store has no concept of.
What is the difference between staleTime and gcTime?
staleTime is how long data is considered fresh; while fresh, React Query will not refetch it on focus or remount. gcTime, previously cacheTime, is how long unused data stays in memory after the last component using it unmounts. Fresh data can be garbage collected, and stale data can still be shown.
How do optimistic updates work in React Query?
In a mutation's onMutate you cancel in-flight queries, snapshot the current cache and write the expected result immediately so the UI responds at once. If the request fails, onError restores the snapshot; onSettled invalidates the query so the server's version wins in the end.
Do I need Redux if I use Context?
Often not. Context solves delivery — getting a value deep into the tree without prop drilling — and pairs fine with useReducer for the transitions. Its weakness is that every consumer re-renders when the value changes, so it suits values that change rarely. Frequently changing shared state is where a store earns its place.
Is Zustand better than Redux?
For most client state it is less code, has no provider requirement and a much smaller bundle. Redux keeps the advantage on devtools, the middleware ecosystem and the predictability of explicit named actions in a large codebase. For a small team with moderate state, Zustand is usually the better trade.

This is 1 of 75+ questions in the React Interview Kit

Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.

⚡ Get the React Interview Kit → ₹399
Written by Arun Karthikeyan · Last updated

Full kit

React Interview Kit · ₹399

Get it →