A Beginner's Guide to NGRX
State management can be a challenging aspect of web application development, particularly in Angular applications. NGRX is a popular solution that makes managing state in Angular applications easier and more efficient. This blog post will walk you through the basics of NGRX, its core concepts, and provide you with some code snippets to get started. Let's dive in!
What is NGRX?
NGRX is a library for Angular applications that provides reactive state management using the Redux pattern. It is built on top of RxJS, a library for reactive programming with JavaScript. NGRX allows you to manage your application state in a predictable and maintainable way, making it easier to develop complex Angular applications.
Core Concepts
Before diving into NGRX, it is essential to understand its core concepts:
Store: The store is a single source of truth for your application state. It is an immutable object that represents the state at any given point in time.
Actions: Actions are plain JavaScript objects that represent different events or actions that can change the state of the application. They have a 'type' property to describe the action and an optional 'payload' property for additional data.
Reducers: Reducers are pure functions that take the current state and an action as arguments and return a new state. They are responsible for changing the state based on the action type.
Selectors: Selectors are functions that extract specific parts of the state for consumption in components. They provide a way to query the state without needing to know its structure.
Effects: Effects are responsible for handling side effects, such as HTTP requests, in response to actions. They are based on observables and are used to interact with external services or APIs.
Getting Started with NGRX
To get started with NGRX, you need to install the required packages:
npm install @ngrx/store @ngrx/effects @ngrx/store-devtools --save
Next, create a simple store for managing a list of items. First, define the state interface:
// app.state.ts
interface AppState {
items: Item[];
}
Now, create an initial state for the store:
// app.state.ts
const initialState: AppState = {
items: [],
};
Next, define the actions to add and remove items from the list:
// item.actions.ts
import { createAction, props } from '@ngrx/store';
export const addItem = createAction(
'[Item] Add Item',
props<{ item: Item }>()
);
export const removeItem = createAction(
'[Item] Remove Item',
props<{ itemId: number }>()
);
Create a reducer function to handle the actions:
// item.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { addItem, removeItem } from './item.actions';
export const itemReducer = createReducer(
initialState,
on(addItem, (state, { item }) => ({ ...state, items: [...state.items, item] })),
on(removeItem, (state, { itemId }) => ({ ...state, items: state.items.filter(item => item.id !== itemId) }))
);
Now, register the store and the reducer in your app.module.ts
:
// app.module.ts
import { StoreModule } from '@ngrx/store';
import { itemReducer } from './item.reducer';
@NgModule({
imports: [
// ...
StoreModule.forRoot({ items: itemReducer }),
],
})
export class
// app.module.ts
import { StoreModule } from '@ngrx/store';
import { itemReducer } from './item.reducer';
@NgModule({
imports: [
// ...
StoreModule.forRoot({ items: itemReducer }),
],
})
export class AppModule { }
Now, let's create a simple component that adds and removes items using the NGRX store:
// app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { addItem, removeItem } from './item.actions';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
constructor(private store: Store) {}
addItem(item: Item) {
this.store.dispatch(addItem({ item }));
}
removeItem(itemId: number) {
this.store.dispatch(removeItem({ itemId }));
}
}
In the component template, create a form for adding items and a list for displaying them:
<!-- app.component.html -->
<form (ngSubmit)="addItem(newItem.value)">
<input #newItem type="text" placeholder="Enter new item" required />
<button type="submit">Add Item</button>
</form>
<ul>
<li *ngFor="let item of items$ | async">
{{ item.name }}
<button (click)="removeItem(item.id)">Remove</button>
</li>
</ul>
Lastly, create a selector to extract the items from the state:
// item.selectors.ts
import { createSelector } from '@ngrx/store';
export const selectItems = createSelector(
(state: AppState) => state.items,
(items: Item[]) => items
);
And use it in the component:
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { addItem, removeItem } from './item.actions';
import { selectItems } from './item.selectors';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
items$: Observable<Item[]>;
constructor(private store: Store) {}
ngOnInit() {
this.items$ = this.store.select(selectItems);
}
addItem(item: Item) {
this.store.dispatch(addItem({ item }));
}
removeItem(itemId: number) {
this.store.dispatch(removeItem({ itemId }));
}
}
That's it! You now have a basic understanding of NGRX and how to use it in an Angular application. There's much more to learn, such as handling side effects with Effects and integrating with Angular Router, but this should give you a solid foundation to build upon.
Happy coding!
Need help implementing NGRX in your Angular project? Our experienced developers at BM Dev Services are here to help! Contact us for a consultation and let's make your application's state management a breeze.
Subscribe to my newsletter
Read articles from Jocelyn Knight directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Jocelyn Knight
Jocelyn Knight
Hi there! My name is Jocelyn, and I am the owner of B&M Development Services. With over 8 years of experience in web application development, I am a highly skilled Senior Software Engineer with a strong focus on front-end technologies. My expertise lies in Angular, Typescript, RxJs, NGRX, and Akita for state management, as well as Jasmine and Karma for front-end testing. I am passionate about effectively managing state in complex applications and have worked with various state management solutions, including NGRX and Akita. I am also fascinated by the intersection of machine learning and software engineering, and I have experience in AI tool development and prompt engineering. With the potential of LLMs like GPT-4, I believe we are on the cusp of a revolutionary shift in the field. As the owner of B&M Development Services, I have built a custom UI administrative dashboard for managing AI music stations using Angular, NGRX state management, and Angular Material, mixed with custom styling. I also built an internal application for the implementation of proprietary software solutions around artificial intelligence-based music mixdowns. Another project I completed was building a custom remote video consultation solution for CVS using Angular version 10, allowing remote pharmacists to provide legally required medical consultations to patients in remote CVS pharmacy locations. This allowed the client to save a significant amount of the cost of employing a pharmacist in slower CVS locations. At B&M Development Services, we take pride in our ability to deliver high-quality work and help organizations achieve their project goals. Whether it's working on the front-end, back-end, or exploring the possibilities of LLMs, we are committed to staying at the forefront of the industry and contributing to meaningful projects. My expertise also extends to Node.js and Express.js, and I have experience with alternative technologies such as Jest and Hapi.js. I am a problem-solver at heart and am always excited to collaborate with organizations on new and exciting opportunities. Thank you for considering B&M Development Services for your project needs. We look forward to the opportunity to work with you!