MobX

Функция makeObservable делает класс отслеживаемым. Функция makeAutoObservable делает класс отслеживаемым по переданному контексту и самостоятельно производит нужные для неё настройки.

Любая созданная функция для MobX - это экшен, который меняет состояние. В отличие от Redux, состояние в данном менеджере можно мутировать. То есть, если мы изменим какое-либо значение, то MobX это заметит и запустит рендеринг компонента.

Сейчас нам нужно создать класс, который будет выполнять определённый функционал на странице (в виде методов). В конце нужно просто экспортировать один инстанс объекта класса.

State / store / counter.ts

import { makeAutoObservable } from 'mobx';  
  
class Counter {  
   // переменная, которая является состоянием  
   count = 0;  
  
   constructor() {  
      // настраивает работу с mobx  
      makeAutoObservable(this);  
   }  
  
   increment() {  
      this.count = this.count + 1;  
   }  
  
   decrement() {  
      this.count = this.count - 1;  
   }  
}  
  
export default new Counter();

Далее нам нужно импортировать наш классовый каунтер в компонент и вставить его функции.
Теперь, чтобы использовать каунтер с функциями, описанными в MobX, нужно обернуть весь компонент в функцию observer, которая отслеживает изменения состояний в компоненте и перерендеривает его.

State / State.tsx

import React from 'react';  
import { useState } from 'react';  
import styles from './State.module.scss';  
import { Button } from '@/components';  
import counter from './store/counter';  
import { observer } from 'mobx-react-lite';  
  
export const State = observer((): JSX.Element => {  
   return (  
      <div className={styles.wrapper}>  
         <h2 className={styles.title}>Счётчик:</h2>  
         <h1 className={styles.num}>{counter.count}</h1>  
         <Button  
            buttonType={'gray'}  
            className={styles.reduce}  
            onClick={() => counter.decrement()}  
         >  
            Уменьшить  
         </Button>  
         <Button  
            buttonType={'purple'}  
            className={styles.increase}  
            onClick={() => counter.increment()}  
         >  
            Увеличить  
         </Button>  
      </div>  
   );  
});

И теперь наш счётчик работает и быстро отзывается на все действия

Далее приведём ещё один пример, где мы сразу добавим данные и реализуем список дел

Todo / store / todo.store.ts

import { makeAutoObservable } from 'mobx';  
  
interface ITodo {  
   id: number;  
   title: string;  
   completed: boolean;  
}  
  
class TodoStore {  
   todos: ITodo[] = [  
      { id: 1, title: 'Сходить в магазин', completed: false },  
      { id: 2, title: 'Купить хлеб', completed: false },  
      { id: 3, title: 'Съесть хлеб', completed: false },  
   ];  
  
   constructor() {  
      makeAutoObservable(this);  
   }  
  
   addTodo(todo: ITodo) {  
      this.todos.push(todo);  
   }  
  
   removeTodo(id: number) {  
      this.todos = this.todos.filter(todo => todo.id !== id);  
   }  
  
   completeTodo(id: number) {  
      this.todos = this.todos.map(todo =>  
         todo.id === id ? { ...todo, completed: !todo.completed } : todo,  
      );  
   }  
}  
  
export default new TodoStore();

Сейчас список дел реализуется почти так же, как и прошлый пример с каунтером. В MobX очень важно, чтобы ключи элементов были уникальными, и чтобы они не являлись индексами массива

TodoList / TodoList.tsx

import React from 'react';  
import { observer } from 'mobx-react-lite';  
import todo from './store/todo.store';  
import styles from './TodoList.module.scss';  
import { Button, Input } from '@/components';  
  
export const TodoList = observer(() => {  
   return (  
      <div className={styles.wrapper}>  
         {todo.todos.map(t => (  
            <div key={t.id} className={styles.todo}>  
               <Input  
                  type={'checkbox'}  
                  checked={t.completed}  
                  onChange={() => todo.completeTodo(t.id)}  
               />  
               <div>{t.title}</div>  
               <Button  
                  className={styles.button}  
                  buttonType={'purple'}  
                  onClick={() => todo.removeTodo(t.id)}  
               >  
                  X  
               </Button>  
            </div>  
         ))}  
      </div>  
   );  
});

И теперь наш список дел удаляет себя и может быть выполненным

Так же нужно сказать, что если нам нужно работать с более глубокими уровнями вложенности, что в моб нужно передать опцию deep

Так же мы можем уточнить внутри оверрайда (второй объект моба):

  • что есть отслеживаемый объект todos: observable,
  • что есть экшены addTodo: action
  • И что является вычисляемым значением computed: значение
constructor() {  
   makeAutoObservable(this, { todos: observable, addTodo: action, computed:  });  
}

Вычисляемые значения - это значения, которые мы считаем в методе, помеченном ключевым словом get. Этот метод обязательно должен вычислять результат каких-то вычислений. Основное преимущество в том, что метод вызывается только тогда, когда один из его параметров изменил своё значение. Такой подход оптимизирует выполнение значений.

get total() {  
   return `Counter = ${this.timer + this.count}`;  
}

Теперь тут применяем функцию

<h1 className={styles.num}>{counter.total}</h1>

Асинхронные экшены. Они используются ровно так же, как и остальные экшены. Тут представлен пример получения списка дел с сервера jsonplaceholder

todo.store.ts

fetchTodos() {  
   fetch('https://jsonplaceholder.typicode.com/todos/')  
      .then(response => response.json())  
      .then(json => {  
         this.todos = [...this.todos, ...json];  
      });  
}

Тут при нажатии кнопки мы получаем данный массив

TodoList.tsx

<Button buttonType={'gray'} onClick={() => todo.fetchTodos()}>  
   Получить список дел  
</Button>

И видим огромный список дел