Job Interview Questions - NgRx

Table of Content

  1. [[#What is NgRx]]
  2. [[#Can you explain the role of actions in NgRx and how they differ from mutations in other state management libraries?]]
  3. [[#What is a reducer, and how does it work in the context of NgRx?]]
  4. [[#What are effects in NgRx, and how they help with handling side effects?]]
  5. [[#What is the difference between select and dispatch in NgRx?]]
  6. [[#How to create feature slice? [docs](https //ngrx.io/guide/store/feature-creators)]]
  7. [[#How to create selectors?]]
  8. [[#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:

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.

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
}