I wrote a Custom State Manager for Angular - STATE GUARDIAN
Introduction
The Custom State Manager, known as “STATE GUARDIAN”, is an innovative tool designed by Michael Egbo. Its primary purpose is to manage state, side effects, and more in Angular applications, ensuring a streamlined and efficient workflow.
Features
1. State Management Store
- Manages and tracks the state of different parts of the application using the CustomStore.
- Allows for selecting specific slices of the state for observation.
dispatch:
- Purpose: Used to dispatch an action to update the state.
- When to Use: Trigger a change in the application state, typically when updating the state based on user interactions or other events.
- Example: When a user logs in, you may dispatch an action to update the user’s login status in the state.
select:
- Purpose: Select and observe specific parts of the application state.
- When to Use: “Listen” to changes in a specific part of the state, allowing you to react to state changes and update your component’s properties accordingly.
- Example: Use select to watch for changes in the user’s login status or other parts of the state and take specific actions in response to those changes.
2. Effects Handling
- Manages side effects, especially asynchronous operations like HTTP requests.
- Updates the state based on the result of these side effects.
3. Middleware Integration
- Intercepts actions to introduce additional behavior or side effects.
- Useful for logging, analytics, or any other cross-cutting concerns.
4. Entity Management Utilities
- Provides utilities to manage collections of entities efficiently.
- Supports operations like adding one or many entities, updating, and deleting entities.
5. Data Normalization
- Transforms nested or complex data structures into a flat, consistent format.
- Helps in efficiently managing and querying data.
6. Caching Mechanism
- Caches frequently accessed data to reduce the need for repeated network requests.
- Improves performance and can provide offline data access.
7. Error Handling
- Handles errors during side effects and updates the state accordingly.
- Provides a consistent way to manage and display errors to users.
8. Flexible API Calls
- The effects service can handle both direct HTTP calls and calls through provided services, offering flexibility in how backend interactions are managed.
9. Immutable State Updates
- Ensures that state updates are immutable, leading to predictable state changes and easier debugging.
10. Integration with External Libraries
- Designed to work seamlessly with libraries like RxJS, making it easier to handle asynchronous operations and reactive programming patterns.
11. Modularity and Reusability
- Components like the store, middleware, and effects are modular, allowing for reuse across different parts of the application or even different applications.
State Guardian Library Documentation
State Guardian is a powerful state management library for Angular applications. It provides various features for managing application state, handling asynchronous effects, managing entities, normalizing data, caching, and more. This documentation will guide you through the installation and usage of State Guardian.
Installation
To install the state-guardian library in your Angular project, you can use npm or yarn:
领英推荐
npm install state-guardian
or
yarn add state-guardian
Usage
After installation, you should import and add the StateGuardianModule to your application's module. Here's an example:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StateGuardianModule } from 'state-guardian';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, StateGuardianModule],
bootstrap: [AppComponent],
})
export class AppModule {}
Example Use Case: Book Management App
- Login: Use the EffectsService for login and set user tokens upon success.
- Fetch Books: After logging in, fetch the books. Use normalization to flatten the data and cache it for faster subsequent access.
- Manage Books: Add, update, or delete books using the EntityManagementService.
- Middleware: Log actions using the middleware, providing insights into the user’s behavior.
1. State Model
Define the state model for our application:
interface AppState {
user: User;
books: Book[];
}
interface User {
id: number;
username: string;
token: string;
}
interface Book {
id: number;
title: string;
author: string;
}
2. Stub Backend Service
Simulate the login process:
@Injectable({
providedIn: 'root'
})
export class StubBackendService {
private users = [
{ id: 1, username: 'user1', password: 'pass1', token: 'fake-token' }
];
private books = [
{ id: 1, title: 'Book 1', author: 'Author 1' },
{ id: 2, title: 'Book 2', author: 'Author 2' }
];
login(credentials: { username: string; password: string }): Observable<User> {
const user = this.users.find(u => u.username === credentials.username && u.password === credentials.password);
if (user) {
return of(user);
} else {
return throwError('Invalid credentials');
}
}
getBooks(): Observable<Book[]> {
return of(this.books);
}
addBook(book: Book): Observable<Book> {
this.books.push(book);
return of(book);
}
}
3. Login Component
Create the login component that uses the EffectsService and CustomStore:
import { CustomStore, EffectsService } from 'state-guardian';
@Component({
// ... component metadata
})
export class LoginComponent {
form: FormGroup;
private LOGIN_INITIAL_STATE: LoginState = {
user: {
isLoggedIn: false,
tokens: {
AccessToken: null,
RefreshToken: null,
},
error: null,
isLoading: false,
}
};
constructor(
private authService: AuthService,
private store: CustomStore<AppState>,
private effectsService: EffectsService
) {this.store.dispatch(() => this.LOGIN_INITIAL_STATE)}
ngOnInit() {
// Subscribe to user state changes
this.store.select('user').subscribe(userState => {
this.isLoading = userState.isLoading;
if (userState.isLoggedIn) {
this.router.navigateByUrl('/home');
}
if (userState.error) {
Swal.fire('Hi...', 'Kindly check your username or password', 'warning');
}
});
}
onSubmit() {
const credentials = this.form.value;
const loginEffectConfig = {
apiCall: () => this.authService.login(credentials),
successHandler: (response: User) => {
this.store.dispatch(() => ({ user: response }));
},
errorHandler: (error) => {
console.error('Login failed:', error.message);
}
};
this.effectsService.handleEffect(loginEffectConfig);
}
}
4. Book Management Component
Handle fetching and managing books:
import { CustomStore, EntityManagementService } from 'state-guardian';
@Component({
// ... component metadata
})
export class BookManagementComponent implements OnInit {
books: Book[];
constructor(
private store: CustomStore<AppState>,
private bookService: BookService,
private entityService: EntityManagementService
) {}
ngOnInit() {
this.books = this.bookService.getBooks();
this.store.dispatch(() => ({ books: this.books }));
}
addBook(newBook: Book) {
this.entityService.addOne('books', newBook);
}
updateBook(updatedBook: Book) {
this.entityService.updateOne('books', updatedBook.id, updatedBook);
}
deleteBook(bookId: number) {
this.entityService.deleteOne('books', bookId);
}
}
6. Middleware Integration
Use the middleware to log actions:
import { MiddlewareService } from 'state-guardian';
@Injectable()
export class LoggingMiddleware {
constructor(private middlewareService: MiddlewareService) {}
logAction(actionType: string, payload: any) {
this.middlewareService.intercept(actionType, payload);
}
}
7. Data Normalization
When fetching nested data, use normalization:
import { NormalizationService } from 'state-guardian';
@Injectable()
export class BookService {
constructor(private normalizationService: NormalizationService) {}
getBooks() {
const nestedBooks = [
{ id: 1, title: 'Title 1', author: { name: 'Author 1' } },
{ id: 2, title: 'Title 2', author: { name: 'Author 2' } }
];
return this.normalizationService.normalize(nestedBooks, 'id');
}
}
This documentation gives a more in-depth overview of how to use the state manager and its features. Adjust paths, method names, and data structures to fit your actual implementation.
Author: Michael Egbo Email: https://www.dhirubhai.net/in/michaelegbo/ Package — https://www.npmjs.com/package/state-guardian
TO Contribute see — https://github.com/michaelegbo/State-Guardian