Job Interview Questions - NgRx
Table of Content
- [[#What is NgRx]]
- [[#Can you explain the role of actions in NgRx and how they differ from mutations in other state management libraries?]]
- [[#What is a reducer, and how does it work in the context of NgRx?]]
- [[#What are effects in NgRx, and how they help with handling side effects?]]
- [[#What is the difference between
select
anddispatch
in NgRx?]] - [[#How to create feature slice? [docs](https //ngrx.io/guide/store/feature-creators)]]
- [[#How to create selectors?]]
- [[#How to register feature?]]
What is NgRx?
NgRx is a state management library for Angular, inspired by Redux. It helps you manage the state of your application in a reactive way. Here are some key concepts:
- Store: This is the centralised state of your application. It holds the entire state tree.
- Actions: These are events that describe something that happened in the application. Actions are dispatched to request a state change.
- Reducers: These are pure functions that take the current state and an action, and return a new state.
- Selectors: These are pure functions used to get slices of the state from the store.
- Effects: These handles side effects, like HTTP calls, and can dispatch new actions based on those effects.
NgRx helps maintain a single source of truth for the application state, making it easier to manage complex state logic. It also makes state changes more predictable and facilitates easier debugging and testing.
Can you explain the role of actions in NgRx and how they differ from mutations in other state management libraries?
Actions are plain objects with with a type and optional payload, and they ensure that state changes are predictable and traceable. They are dispatched to trigger changes in the state, and they are connected with reducers and effects.
export const login = createAction('[Login Page] User Login', props<{ user: User }>());
export const logout = createAction('[Top Menu] User Logout');
What is a reducer, and how does it work in the context of NgRx?
Reducers are pure functions, meaning that they don't have side effects and always return the same output given the same input. They take the current state and an action, and return a new state without mutating the old state.
export interface AuthState {
user: User;
}
const initialAuthState: AuthState = {
user: undefined;
}
export const authReducer: ActionReducer<AuthState> = createReducer(
initialAuthState,
on(AuthActions.login, (state, action): AuthState => ({
user: action.user
})),
on(AuthActions.logout, (state, action): AuthState => ({
user: undefined
}))
);
What are effects in NgRx, and how they help with handling side effects?
Effects handle side effects like HTTP requests and can dispatch new actions based on the results. They help keep the logic for side effects separate from the reducers.
export const login$ = createEffect(
(actions$ = inject(Actions)) =>
actions$.pipe(
ofType(AuthActions.login),
tap((action) => localStorage.setItem('user', JSON.stringify(action.user)))
),
{ dispatch: false }
);
export const logout$ = createEffect(
(actions$ = inject(Actions), router = inject(Router)) =>
actions$.pipe(
ofType(AuthActions.logout),
tap(() => {
localStorage.removeItem('user');
router.navigateByUrl('/login');
})
)
)
What is the difference between select
and dispatch
in NgRx?
Select
is used to retrieve data from the store, essentially reading the state, while dispatch
is used to send actions that can modify the state or trigger side effects.
How to create feature slice? docs
import { createFeature, createReducer, on } from '@ngrx/store';
import { Book } from './book.model';
import * as BookListPageActions from './book-list-page.actions';
import * as BooksApiActions from './books-api.actions';
interface State {
books: Book[];
loading: boolean;
query: string;
}
const initialState: State = {
books: [],
loading: false,
query: '',
};
export const booksFeatureKey = 'books';
export const booksFeature = createFeature({
name: booksFeatureKey,
reducer: createReducer(
initialState,
on(BookListPageActions.enter, (state) => ({
...state,
loading: true,
})),
on(BookListPageActions.search, (state, action) => ({
...state,
query: action.query
})),
on(BooksApiActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false
}))
),
extraSelectors: ({ selectQuery, selectBooks }) => ({
selectFilteredBooks: createSelector(
selectQuery,
selectBooks,
(query, books) => books.filter(book => book.title.includes(query)),
),
selectFilteredBooksWithRating = createSelector(
selectFilteredBooks,
(books) => books.filter((book) => book.ratingCount >= 1)
)
}),
});
export const {
name,
reducer,
selectBooksState,
selectBooks,
selectLoading,
} = booksFeature;
How to create selectors?
import { createSelector } from '@ngrx/store';
import { booksFeature } from './books.reducer';
export const selectBookListPageViewModel = createSelector(
booksFeature.selectBooks,
booksFeature.selectLoading,
(books, loading) => ({ books, loading })
)
How to register feature?
Class approach
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { booksFeature } from './books.reducer';
@NgModule({
imports: [StoreModule.forFeature(booksFeature)],
})
export class BooksModule {}
Using the Standalone API
import { bootstrapApplication } from '@angular/platform-browser';
import { provideStore, provideState } from '@ngrx/store';
import { AppComponent } from './app.component';
import { booksFeature } from './books.reducer';
bootstrapApplication(AppComponent, {
providers: [
provideStore(),
provideState(booksFeature),
],
});
Registering in the providers
array of route config
import { Route } from '@angular/router';
import { provideState } from '@ngrx/store';
import { booksFeature } from './books.reducer';
export const routes: Route[] = [
{
path: 'books',
providers: [
provideState(booksFeature)
]
}
]
Can't use optional properties when creating feature. Each optional symbol need to be replaced with | null
or | undefined
interface State {
books: Book[];
activeBookId: string | undefined
}
const initialState: State = {
books: [],
activeBookId: undefined
}