skip to content

In React-Redux, what do Provider, useSelector and useDispatch each do when you connect a Redux store to a React app?

level: juniorimportance: must knowfreq 74%

answer

  1. one component, two everyday hooks
  2. context carries the store
  3. read and subscribe, then compare
  4. dispatch is the store's own

basics

~20 s

Provider makes the Redux store available to every component beneath it. useSelector reads a value from the store and re-renders the component when that value changes. useDispatch returns the store's dispatch function so components can send actions.

solid answer

~40 s

`<Provider store={store}>` is rendered once near the root; it puts the store in a React context and sets up the subscription that the hooks attach to. `useSelector(selector)` calls your selector with the current state, returns the result, and **subscribes**: after each dispatched action it re-runs the selector and re-renders the component only if the new result differs from the previous one, by `===` unless you pass an equality function. `useDispatch()` returns the store's `dispatch`, a stable reference for the life of the store, which you call with an action or a thunk. A component using either hook outside a `Provider` throws *could not find react-redux context value*. In TypeScript, React-Redux 9.1 added `useSelector.withTypes<RootState>()` and `useDispatch.withTypes<AppDispatch>()` to create pre-typed hooks once.

code

tsx · 23 lines
tsx
import { Provider, useDispatch, useSelector } from 'react-redux'
import { createRoot } from 'react-dom/client'
import { store, type RootState, type AppDispatch } from './store'
import { itemAdded, selectCartCount } from './cartSlice'

export const useAppSelector = useSelector.withTypes<RootState>()
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()

function CartButton({ productId }: { productId: string }) {
  const count = useAppSelector(selectCartCount)
  const dispatch = useAppDispatch()
  return (
    <button onClick={() => dispatch(itemAdded(productId))}>
      Add to cart ({count})
    </button>
  )
}

createRoot(document.getElementById('root')!).render(
  <Provider store={store}>
    <CartButton productId="p-1" />
  </Provider>,
)

go deeper

for a junior

Recall the three roles: Provider exposes the store once, useSelector reads and subscribes, useDispatch returns dispatch for sending actions and thunks.

for a middle

Explain that useSelector re-runs the selector after each dispatch and compares results with ===, and why useStore does not subscribe.

for a senior

Set up typed hooks with withTypes, test components with a real store in a Provider, and keep state flowing through selectors rather than ad hoc store reads.

for a principal

Decide how the store is provided across app shells, tests and server rendering, including when a per-request store and serverState are needed.

## The three pieces React-Redux is the official binding between a Redux store and React components. Day to day you use one component and two hooks. | API | Where it is used | What it does | |---|---|---| | `<Provider store={store}>` | once, near the root of the tree | exposes the store to every descendant through React context | | `useSelector(selector)` | any function component | reads a value from the store and subscribes to changes | | `useDispatch()` | any function component | returns the store's `dispatch` | ## Provider `Provider` takes the store created by `configureStore` and renders its children. Internally it places the store and a root **subscription** object into React-Redux's context, so hooks anywhere below can find both. - There is normally exactly **one** `Provider`, wrapping the whole app, because Redux has one store. - It also accepts `serverState`, used during hydration after server rendering so the first client render reads the same state the server used. - A component that calls `useSelector` or `useDispatch` with no `Provider` above it throws *"could not find react-redux context value; please ensure the component is wrapped in a <Provider>"*. In tests, wrap the component under test in a `Provider` with a test store. The context carries the store **instance**, which never changes; state changes flow through the subscription, not through context re-renders. ## useSelector `useSelector(selector, equalityFnOrOptions?)` does two jobs: 1. **Read.** During render it calls `selector(state)` and returns the result. 2. **Subscribe.** It registers with the store; after every dispatched action it runs the selector again and compares the new result with the previous one. If they differ, the component re-renders; if they are equal, nothing happens. The default comparison is strict reference equality, `===`. That makes selectors that return an existing piece of state — `state.cart.items`, `state.user.name` — cheap and precise. A selector that builds a new object or array on every call looks changed every time, which is a separate topic worth knowing well. You can call `useSelector` several times in one component, one per value; each call is its own subscription. ## useDispatch `useDispatch()` returns the store's `dispatch` function. It is the same function reference for as long as the store is the same, so it is safe to use in effect dependency arrays and does not need memoising itself. ```tsx const dispatch = useAppDispatch() <button onClick={() => dispatch(itemAdded(product))}>Add</button> ``` With Redux Toolkit's default middleware, `dispatch` also accepts thunks, so `dispatch(fetchCart())` works the same way. ## Pre-typed hooks In TypeScript, repeating `(state: RootState) => ...` and `AppDispatch` at every call site is noisy and error-prone. React-Redux 9.1 added `.withTypes()`: ```ts export const useAppSelector = useSelector.withTypes<RootState>() export const useAppDispatch = useDispatch.withTypes<AppDispatch>() export const useAppStore = useStore.withTypes<AppStore>() ``` Components then import `useAppSelector` and `useAppDispatch`. Typing `useDispatch` with the store's own dispatch type is what makes dispatching thunks type-check. ## useStore, the rarely needed fourth `useStore()` returns the store itself. It does **not** subscribe, so reading `store.getState()` during render shows stale data after updates. It exists for rare cases such as injecting reducers; prefer `useSelector` for reading state. ## Batching When one dispatch changes values read by several components, React 18 and later batch the resulting updates into one render pass automatically. React-Redux 9 still exports `batch()`, but only as a deprecated no-op kept for older code. ## Testing components that use the hooks Because the hooks read the store from context, a component test wraps the component in a `Provider` with a **real store** built from the app's reducers, for example `render(<Provider store={makeStore(preloadedState)}><CartButton productId="p-1" /></Provider>)`. The test then asserts on the rendered output and on `store.getState()` after a click. - Create a **fresh store per test**; a store shared across tests leaks state between them. - Prefer this over mocking `useSelector`, which only tests the mock and skips the selector and reducer wiring the component depends on. - A small `renderWithProviders` helper keeps the setup to one line per test. ## Putting it together - create the store with `configureStore`; - wrap the app in `<Provider store={store}>`; - read with `useAppSelector(selectSomething)`; - write with `useAppDispatch()` and action creators or thunks. An interview answer should cover all three roles, name the default `===` comparison, and mention the typed hooks.

  • Why should a React-Redux component not read state with useStore().getState() during render?
    `useStore` returns the store without subscribing to it, so the component is not re-rendered when state changes and keeps showing whatever it read last. `useSelector` both reads and subscribes, so it is the right tool for rendering from store state.
  • Is the function returned by React-Redux's useDispatch stable between renders?
    Yes. It is the store's own `dispatch`, so its reference stays the same as long as the `Provider` keeps the same store. Listing it in a `useEffect` dependency array therefore does not cause extra effect runs.

saying these in an interview costs you the question

  • Each component needs its own Provider around it
  • useSelector deep-compares the selected value by default
  • useDispatch returns a new function on every render
  • useStore().getState() keeps the component up to date
  • Provider re-renders the whole tree on every state change