35 questions - Intermediate to advanced

NgRx interview questions and answers

NgRx questions are really questions about the Redux pattern used in Angular. Interviewers want to see 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 too much.

Last updated:

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 store that you never change directly. The only way to change it is to dispatch an action that a pure reducer handles.

What problem does NgRx solve?

In a big app the same data is needed in many unrelated components. Passing it around, or copying it into several services, ends with screens that disagree with each other. NgRx gives one source of truth, one clear way to update it, 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 make the next state. Selectors read parts of the state. Effects do side effects such as HTTP and then dispatch more actions.

Explain the NgRx data flow in one sentence.

A component dispatches an action, the reducer makes new state from the old state and that action, selectors push the changed part 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 changes only by dispatching actions. 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 used by one component only. When the data is really a cache of the server. Adding NgRx to a small app gives a lot of extra code and 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 tools: one strict update path, time-travel devtools, effects for async flows, memoised selectors and easy tests. A service is simpler, but every team member can change state in their own way.

Actions and reducers

What is an action?

An action is a plain object with a unique type string and optional data. It says something happened. Actions should describe events, not commands: "Login Page: Login Submitted", not "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 read the devtools log, so you debug a flow much faster. It also stops people reusing one action from many places, which makes reducers hard to predict.

What is a reducer?

A reducer is a pure function that takes the current state and an action and returns the next state. It must not change the state in place, 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?

Being pure makes it easy to test and lets devtools replay actions. Never changing state in place lets Angular OnPush and NgRx selectors spot a change by comparing references, which is fast. If you change state in place, the UI simply does not update.

Can one action be handled by several reducers?

Yes. Every reducer sees every action. So one "Logout" action can reset several feature slices at the same time. That is a correct use of a shared action.

What is a meta-reducer?

A meta-reducer wraps the root reducer and sees every action before or after it. It is used for logging, for clearing 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. Dispatch only when shared state really changes, or when a side effect must start. Local UI state, like whether an accordion is open, should stay inside the component.

Selectors

What is a selector?

A selector is a function that reads a part of the state. createSelector builds a memoised selector. It runs again only when its inputs change, and otherwise returns the same reference. That stops OnPush components from rendering for no reason.

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 its last result. If the inputs are the same references, it returns the saved result and does not run the function again. That is why you should work out derived data in selectors, not in the component.

Why should you not compute derived state in a reducer?

Because it copies data that can go out of date, and it makes the state bigger. Store the least you can and work out the rest in selectors.

What is createFeatureSelector?

It returns a selector for one whole feature slice by its registered key. Feature selectors then do not need to know where that slice sits in the global state tree.

How do you write a selector that takes a parameter?

Return a function from the selector using props, or better, use a factory function that creates a selector per id. Be careful: a factory selector loses its 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 the stream of actions, does the side effect - usually an HTTP call - and then dispatches a new action with the result. It keeps reducers pure by taking 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 newest matters, like a search. concatMap for writes where order matters. exhaustMap for a login or a save, so a double click does not fire twice. mergeMap when the calls are independent and can run together.

What is an effect with dispatch: false?

It is 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 an error.

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

How do you read state inside an effect?

Use concatLatestFrom or withLatestFrom on a selector. concatLatestFrom is better, because it reads the state only when the action actually arrives.

How do you test an effect?

Give it a fake actions stream with provideMockActions, stub the API service, send the action in, and check the action that comes out. An effect is just Observable in and Observable out, so it is easy to test.

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 then shows the same action over and over. Fix it by making the output action different from the input action.

Entity, feature APIs and alternatives

What is NgRx Entity?

NgRx Entity is a helper for storing collections. It keeps items in a normalised shape: an ids array plus an entities map. It also gives ready-made addOne, upsertMany, updateOne and removeOne functions and 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 finding or updating one item through the entities map is O(1), instead of scanning an array. And the same item is not copied into several places, so the copies cannot go out of sync.

What is createFeature?

createFeature is a newer API. It puts the reducer and the slice name together and then creates the feature selector plus one selector per state property for you. It removes a lot of repeated selector code.

What is ComponentStore?

ComponentStore is 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 never enters the global store. It is a good middle step when the global store feels too heavy.

What is the NgRx Signal Store?

It is a newer store built on Angular signals instead of Observables. You read state through signals at once, computed values take the place of selectors, and it needs far less code than the classic store, while keeping the same one-way flow.

What is Redux DevTools and why is it useful?

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

How do you handle loading and error state in NgRx?

Keep loading and error fields in the feature slice. Set loading to true on the request action, and to false on both success and failure. Store the message on failure. Then expose both as selectors so the template can show a spinner or an error.

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. Think about ComponentStore or the Signal Store for smaller features. Do not put everything in the global store just because it is there.

What is the difference between NgRx and React Redux Toolkit?

The pattern is the same. 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. The ideas move straight across.

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.