📜 ⬆️ ⬇️

Manage Angular Health with Mobx

State managment


Every developer knows that state management is quite a complicated thing. Constantly keeping track of what and when has changed is just a nightmare, especially in large applications.


In the world of Angular, there are several solutions that can make managing a state less complicated, painful, and fragile.


The two most popular solutions are ngrx/store , inspired mostly by Redux, and Observable data services .


Personally, I really like Redux, and it is worth every line of boilerplate code. But, unfortunately, some may disagree with me or Redux is not particularly applicable in their applications.


Therefore, I decided to tell you how Mobx can be useful in solving the problem of managing a condition. The idea is to combine the two worlds, Redux and Mobx.


So let's take the Redux Immunity, the power of Rx + ngrx, and the ability to control the Mobx state. This combination will allow you to use asynchronous pipes in combination with OnPush strategy in order to achieve the greatest performance.


Before we begin, it is understood that you have sufficient knowledge of Mobx and Angular.


For simplicity, we will create a traditional Tuda application. Well, let's start?


Story


I want to adhere to the principle of common responsibility , so I create stor for the filter and tudushek (you can combine them into one, if necessary).


Let's create a filter stack.


 import { Injectable } from '@angular/core'; import { action, observable} from 'mobx'; export type TodosFilter = 'SHOW_ALL' | 'SHOW_COMPLETED' | 'SHOW_ACTIVE'; @Injectable() export class TodosFilterStore { @observable filter = 'SHOW_ALL'; @action setFilter(filter: TodosFilter) { this.filter = filter; } } 

Add a stop to this tudus.


 export class Todo { completed = false; title : string; constructor( { title, completed = false } ) { this.completed = completed; this.title = title; } } @Injectable() export class TodosStore { @observable todos: Todo[] = [new Todo({ title: 'Learn Mobx' })]; constructor( private _todosFilter: TodosFilterStore ) {} @action addTodo( { title } : Partial<Todo> ) { this.todos = [...this.todos, new Todo({ title })] } @computed get filteredTodos() { switch( this._todosFilter.filter ) { case 'SHOW_ALL': return this.todos; case 'SHOW_COMPLETED': return this.todos.filter(t => t.completed); case 'SHOW_ACTIVE': return this.todos.filter(t => !t.completed); } } } 

If you are familiar with Mobx, the code above will seem pretty simple.


Note that it’s good practice to always use @action decorator. He helps to adhere to the concept of "Do not change the state directly," known to us since Redux. The Mobx docks say:


In strict mode, it is not allowed to change the status outside the action.

RxJS Bridge


One of the coolest pieces of RxJS is the ability to convert any data source to RxJS Observable. In our case, we will use the computed function from Mobx to listen to the change of the state and give it to our subscribers in the Observable.


 import { Observable } from 'rxjs/Observable'; import { computed } from 'mobx'; export function fromMobx<T>( expression: () => T ) : Observable<T> { return new Observable(observer => { const computedValue = computed(expression); const disposer = computedValue.observe(changes => { observer.next(changes.newValue); }, true); return () => { disposer && disposer(); } }); } 

In Rx computed something like BehaviorSubject mixed with distinctUntilChanged()


Every time a change occurs (change by reference) in an expression, a callback is executed, which passes the new value to our subscribers. Now we have a bridge between Mobx and Rx.


Component tudushki


Let's create a component that accepts Input() and will emit an event when selected.
Please note that here we use the onPush strategy to identify changes.


 @Component({ selector: 'app-todo', template: ` <input type="checkbox" (change)="complete.emit(todo)" [checked]="todo.completed"> {{todo.title}} `, changeDetection: ChangeDetectionStrategy.OnPush }) export class TodoComponent { @Input() todo: Todo; @Output() complete = new EventEmitter(); } 

Component list tudushek


Let's create a component of the list of inputs that accepts Input() and will emit an event when something is selected.
Please note that here we use the onPush strategy to identify changes.


 @Component({ selector: 'app-todos', template: ` <ul> <li *ngFor="let todo of todos"> <app-todo [todo]="todo" (complete)="complete.emit($event)"> </app-todo> </li> </ul> `, changeDetection: ChangeDetectionStrategy.OnPush }) export class TodosComponent { @Input() todos: Todo[] = []; @Output() complete = new EventEmitter(); } 

Component pages tudushek


 @Component({ selector: 'app-todos-page', template: ` <button (click)="addTodo()">Add todo</button> <app-todos [todos]="todos | async" (complete)="complete($event)"> </app-todos> ` }) export class TodosPageComponent { todos : Observable<Todo[]>; constructor( private _todosStore: TodosStore ) { } ngOnInit() { this.todos = fromMobx(() => this._todosStore.filteredTodos); } addTodo() { this._todosStore.addTodo({ title: `Todo ${makeid()}` }); } } 

If you worked with ngrx/store , you will feel at home. The todos property is Rx Observable and will only work when the filteredTodos property changes in our store.


The filteredTodos property is a computed value that triggers a change if there is a net change in the filter or in the todos property of our site.


And of course, we get all the Rx buns such as combineLatest() , take() , etc., since now this is the Rx stream.


It's all. Here is a ready example .


I think you liked this small concept, I hope you were interested.


noticed ochepyatku in lichku


')

Source: https://habr.com/ru/post/341564/


All Articles