35 questions - Intermediate to advanced

NgRx interview questions and answers

NgRx questions are really questions about the Redux pattern applied to Angular. Interviewers want to know that you understand one-way data flow, that reducers must be pure, that side effects live in effects, and - just as important - that you know when NgRx is overkill.

Core ideas

What is NgRx?

NgRx is a state management library for Angular built on the Redux pattern and RxJS. All shared state lives in one immutable store, and the only way to change it is to dispatch an action that a pure reducer handles.

What problem does NgRx solve?

In a large app, the same data is needed in many unrelated components, and passing it around or duplicating it in services leads to screens that disagree with each other. NgRx gives one source of truth, a predictable update path and a full history of what happened.

What are the main building blocks?

Store holds the state. Actions describe events. Reducers are pure functions that produce the next state. Selectors read slices of state. Effects handle side effects like HTTP and then dispatch more actions.

Explain the NgRx data flow in one sentence.

A component dispatches an action, the reducer produces new state from the old state and that action, selectors push the changed slice back into the component, and any effect listening to that action can do async work and dispatch a follow-up action.

What are the three principles of the Redux pattern?

One single source of truth, state is read-only and only changed by dispatching actions, and changes are made by pure reducer functions with no side effects.

When should you NOT use NgRx?

When the app is small, when the state is only used by one component, or when the data is really server cache. Adding NgRx to a small app is a lot of boilerplate for no benefit. A service with a BehaviorSubject or a signal is often enough.

What is the difference between NgRx and a service with a BehaviorSubject?

Both share state. NgRx adds structure and tooling: a strict update path, time-travel devtools, effects for async flows, memoised selectors and easy testing. A service is simpler but every team member can change state in their own way.

Actions and reducers

What is an action?

A plain object with a unique type string and optional data, describing something that happened. Actions should describe events, not commands - "Login Page: Login Submitted" rather than "Set User".

export const loadUsers = createAction("[Users Page] Load Users");

export const loadUsersSuccess = createAction(
  "[Users API] Load Users Success",
  props<{ users: User[] }>()
);

Why is the "[Source] Event" naming convention used?

Because the source tells you where the action came from when you look at the devtools log, so debugging a flow is much faster. It also stops people reusing one action from many places, which makes reducers unpredictable.

What is a reducer?

A pure function that takes the current state and an action and returns the next state. It must not mutate the state, must not call APIs and must not use random values or the current time - the same input must always give the same output.

export const usersReducer = createReducer(
  initialState,
  on(loadUsers, (state) => ({ ...state, loading: true })),
  on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),
  on(loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false }))
);

Why must a reducer be pure and immutable?

Purity makes it easy to test and lets devtools replay actions. Immutability lets Angular OnPush and NgRx selectors detect a change by comparing references, which is fast. Mutating state means the UI silently does not update.

Can one action be handled by several reducers?

Yes. Every reducer sees every action, so a single "Logout" action can reset several feature slices at once. That is a proper use of a shared action.

What is meta-reducer?

A function that wraps the root reducer and sees every action before or after it. It is used for logging, for resetting the whole state on logout, and for saving and restoring state from localStorage.

What is the difference between an action and an event in your UI?

Not every click needs an action. Only dispatch when shared state actually changes or when a side effect must start. Local UI state, like whether an accordion is open, should stay in the component.

Selectors

What is a selector?

A function that reads a piece of state. createSelector builds a memoised selector, so it only recalculates when its inputs change and returns the same reference otherwise - which keeps OnPush components from re-rendering.

export const selectUsersState = createFeatureSelector<UsersState>("users");

export const selectAllUsers = createSelector(
  selectUsersState,
  (state) => state.users
);

export const selectActiveUsers = createSelector(
  selectAllUsers,
  (users) => users.filter((u) => u.active)
);

What is memoisation in a selector?

The selector remembers its last inputs and last result. If the inputs are the same references, it returns the cached result without running the function again. That is why derived data should be computed in selectors, not in the component.

Why should you not compute derived state in a reducer?

Because it duplicates data that can go stale and makes the state bigger. Store the minimum, derive the rest in selectors.

What is createFeatureSelector?

It returns a selector for one whole feature slice by its registered key, so feature selectors do not need to know where they sit in the global state tree.

How do you write a selector that takes a parameter?

Return a function from the selector using props, or - the cleaner modern way - use a factory function that creates a selector per id. Be careful, because a factory selector loses memoisation if you create it inside the template.

export const selectUserById = (id: string) =>
  createSelector(selectAllUsers, (users) => users.find((u) => u.id === id));

Effects

What is an effect?

An effect listens to a stream of actions, does something with a side effect - usually an HTTP call - and then dispatches a new action with the result. It keeps reducers pure by moving all async work out of them.

loadUsers$ = createEffect(() =>
  this.actions$.pipe(
    ofType(loadUsers),
    switchMap(() =>
      this.api.getUsers().pipe(
        map((users) => loadUsersSuccess({ users })),
        catchError((error) => of(loadUsersFailure({ error: error.message })))
      )
    )
  )
);

Why must catchError be inside the inner pipe?

Because an error on the outer actions stream kills the effect for good - it will never react to that action again. Catching inside the inner Observable means only that one request fails and the effect keeps listening.

Which flattening operator should an effect use?

switchMap for reads where only the latest matters, like a search. concatMap for writes where order matters. exhaustMap for a login or save, so double clicks do not fire twice. mergeMap when the calls are independent and can run in parallel.

What is an effect with dispatch: false?

An effect that only does something and does not produce a new action - navigating with the router, showing a toast, writing to localStorage. Without that option, NgRx would try to dispatch undefined and throw.

redirect$ = createEffect(
  () => this.actions$.pipe(ofType(loginSuccess), tap(() => this.router.navigate(["/dashboard"]))),
  { dispatch: false }
);

How do you read state inside an effect?

With concatLatestFrom or withLatestFrom on a selector. concatLatestFrom is preferred because it reads the state lazily, only when the action actually arrives.

How do you test an effect?

Provide a mock actions stream with provideMockActions, stub the API service, send the action in, and assert on the action that comes out. Because the effect is just an Observable in, Observable out, it is very testable.

What is an infinite loop in effects and how does it happen?

An effect listens to action A and dispatches action A again, directly or through another effect. The devtools log shows the same action repeating. Fix it by making the output action distinct from the input.

Entity, feature APIs and alternatives

What is NgRx Entity?

A helper for storing collections. It keeps items in a normalised shape - an ids array plus an entities map - and gives ready-made addOne, upsertMany, updateOne and removeOne functions plus selectors, so you do not write array logic by hand.

const adapter = createEntityAdapter<User>();
export const initialState = adapter.getInitialState({ loading: false });

on(loadUsersSuccess, (state, { users }) => adapter.setAll(users, { ...state, loading: false }));

Why is normalised state better for large lists?

Because looking up or updating one item is O(1) through the entities map, instead of scanning an array, and the same item is not duplicated in several places, so it cannot get out of sync.

What is createFeature?

A newer API that groups the reducer and the state slice name together and generates the feature selector plus one selector per state property automatically. It removes a lot of repetitive selector code.

What is ComponentStore?

A small local store for one component or one feature. It has state, updaters, selectors and effects, but it lives and dies with the component and does not go into the global store. It is a good middle ground when global state is too heavy.

What is the NgRx Signal Store?

A newer store built on Angular signals instead of Observables. State is read synchronously through signals, computed values replace selectors, and it needs far less boilerplate than the classic store while keeping the same one-way flow.

What is Redux DevTools and why is it useful?

A browser extension that shows every action, the state before and after, and lets you jump back to any point in time. It turns "the screen is wrong" into "this action produced the wrong state", which is much faster to debug.

How do you handle loading and error state in NgRx?

Keep loading and error flags in the feature slice, set loading true on the request action, false on both success and failure, and store the error message on failure. Then expose them as selectors so the template can show a spinner or a message.

How do you avoid the boilerplate complaint about NgRx?

Use createActionGroup and createFeature, use Entity for collections, keep local UI state out of the store, and consider ComponentStore or the Signal Store for smaller features. Do not put everything in the global store just because it exists.

What is the difference between NgRx and React Redux Toolkit?

The pattern is identical. NgRx is built around RxJS and Angular dependency injection, uses effects for async work and createSelector for memoisation. Redux Toolkit uses thunks or listeners and Immer for updates. Concepts transfer directly between the two.

Continue with another topic

Practise live

CrackInterviewAI listens to the live interview and gives a structured answer on screen for coding, system design, HR and project questions. Download for Windows.