В этом посте мы рассмотрим основные концепции mobx на готовых примерах.

Введение. Mobx - это простая, хорошо протестированная библиотека с управлением состоянием, которая делает компоненты реактивными. Это на удивление быстро и последовательно. С Mobx вы можете создать несколько магазинов в вашем проекте.

Основные концепции Mobx:

  • Наблюдаемые состояния
  • Вычисленные значения на основе наблюдаемых состояний
  • Реакции

Давайте начнем . . .

Наблюдаемый:

Функция observable () автоматически преобразует объект, массив или карту в наблюдаемую сущность.

  1. Использование по умолчанию: let list = observable ({item = 0})
  2. Объект: let list = observable.object ({item = 0})
  3. Массив: let list = observable.array ([])
  4. Карта: let list = observable.map (value);
  5. Примитивы, функции и экземпляры классов:
    let item = observable.box (value)

Observable.ref: он будет отслеживать изменения ссылок на объект наблюдаемых объектов (объект, массив, карта).

@observable.ref isUserAuthorized= null;
--------------------------------------------------------------------
class Modal {
  @observable.ref user = {
    name: "Tom",
    age: 30
  };
  constructor() {
    autorun(() => console.log(this.user, "autorun"));
  }
  @action
  updateUser({ name, age }) {
    this.user = { name, age };
  }
}
const store = new Modal();
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 500);
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 1000);
setTimeout(() => store.updateUser({ name: "Morjim", age: 28 }), 1500);
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 2000);

********************************************************************
Prints: Autorun for every change in observable object even if the values are same.
// {name: "Tom", age: 30} "autorun" 
// {name: "Tom", age: 30} "autorun" 
// {name: "Tom", age: 30} "autorun" 
// {name: "Morjim", age: 28} "autorun" 
// {name: "Tom", age: 30} "autorun"

Observable.struct: он выполнит структурную проверку, которая означает, что свойства вашего объекта сравниваются, а затем решает, есть ли изменения.

@observable.struct user = {name: "Tom", age: 30};
--------------------------------------------------------------------
class Modal {
  @observable.struct user = {
    name: "Tom",
    age: 30
  };
  constructor() {
    autorun(() => console.log(this.user, "autorun"));
  }
  @action
  updateUser({ name, age }) {
    this.user = { name, age };
  }
}
const store = new Modal();
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 500);
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 1000);
setTimeout(() => store.updateUser({ name: "Morjim", age: 28 }), 1500);
setTimeout(() => store.updateUser({ name: "Tom", age: 30 }), 2000);

********************************************************************
Prints: Only change happen when previous values not matching as whole with the new values. In short, for a unique change.
// {name: "Tom", age: 30} "autorun" 
// {name: "Morjim", age: 28} "autorun" 
// {name: "Tom", age: 30} "autorun"

Глубокая наблюдаемость: MobX применит глубокую наблюдаемость к наблюдаемому экземпляру как по умолчанию. будут случаи, когда вы можете не захотеть отслеживать изменение состояния за пределами первого уровня. Этого можно добиться, передав в качестве опции {deep: false}.

observable.object(value, decorators, { deep: false });
observable.map(values, { deep: false });
observable.array(values, { deep: false });

Observable.shallow: Это похоже на установку параметра {deep: false} для наблюдаемого объекта.

@observable.shallow items = {};
--------------------------------------------------------------------
class Modal {
  @observable.shallow user = {
    shortName: "Talt",
    age: 30,
    fullName: {
      firstName: "",
      lastName: ""
    }
  };
  constructor() {
    autorun(() => console.log(Object.values(this.user), "autorun"));
    reaction(
      () => Object.values(this.user),
      data => console.log(data, "reaction")
    );
  }
  @action
  editShortName() {
    this.user.shortName = "Molfer";
  }
  @action
  updateFullName(firstName, lastName) {
    this.user.fullName = { firstName, lastName };
  }
@action
  editFullName() {
    this.user.fullName.firstName = "Molly";
    this.user.fullName.lastName = "Ferguson";
  }
}
const store = new Modal();
setTimeout(() => store.editFullName(), 1000);
setTimeout(() => store.editShortName(), 2000);

********************************************************************
Prints:
// ["Talt", 30, {firstName: "", lastName: ""}] "autorun"
// ["Molfer",30, {firstName: "Molly",lastName: "Ferguson"}] "reaction"
// ["Molfer",30, {firstName: "Molly",lastName: "Ferguson"}] "autorun"
// NO REACTION ON editing FULL NAME
@observable.shallow items = [];
-------------------------------------------------------------------
class Modal {
  @observable.shallow cart = [];
  constructor() {
    autorun(() => console.log(this.cart.toJS(), "autorun"));
    reaction(() => this.cart, data => console.log(data,"reaction"));
    reaction(() => this.cart.length,
      data => console.log(data, "reaction on length"));
  }
  @action
  addItem(data) {
    this.cart.push(data);
  }
}
const store = new Modal();
setTimeout(() => store.addItem("10"), 1000);
setTimeout(() => store.addItem({ Name: "Malice" }), 2000);
Prints:
// [] "autorun" 
// 1 "reaction on length" 
// ["10"] "autorun" 
// 2 "reaction on length" 
// ["10", Object] "autorun"

Observable.shallowBox: предназначен для создания примитивов, функций и экземпляров классов в неглубокую наблюдаемую сущность.

const box = observable.shallowBox(value) or 
observable.box(value, { deep: false })

Рассчитано

Они получают свою ценность из других наблюдаемых. Если любой из этих зависимых наблюдаемых изменяется, вычисляемое свойство также изменяется.

class FrontLine {
 @observable items = 10;
 @observable amount = 150;
 constructor(items) {
  this.items = items;
 }
 @computed get total() {
  return this.items * this.amount;
 }
}
const storeFront = new FrontLine(30);
console.log(storeFront.total);

********************************************************************
Prints: In computed both items and amount are dependent observables.
// 4500

Computed.struct: Это необходимо для того, чтобы для уведомления учитывалось только реальное изменение структуры объекта.

class Model {
  @observable x = 10;
  @observable y = 150;
  constructor(data) {
    this.x = data;
    autorun(() => console.log(this.add, "autorun"));
    reaction(
      () => this.add,
      () => console.log(this.add, `reaction on ${this.x} &
            ${this.y}`));
  }
  @computed.struct get add() {
    return {
      val: this.x + this.y || 0
    };
  }
  @action
  updateData({ x, y }) {
    this.x = x;
    this.y = y;
  }
}
const store = new Model();
setTimeout(() => store.updateData({ x: 200, y: 100 }), 1000);
setTimeout(() => store.updateData({ x: 100, y: 200 }), 2000);
setTimeout(() => store.updateData({ x: 101, y: 200 }), 2000);
********************************************************************
Prints:
// {val: 0} "autorun" 
// {val: 300}"reaction on 200 & 100" 
// {val: 300} "autorun"
// {val: 301} "reaction on 101 & 200" 
// {val: 301} "autorun"

Действие:

Это гарантирует, что все уведомления об изменениях состояния запускаются, но только после завершения функции действия.

action & action.bound (предварительно привязывает экземпляр класса к методу)

class githubReposList {
  repos = observable.array([]);
  showError = observable.box(false, { deep: false });
  constructor() {
    reaction(
      () => this.repos.length,
      () => console.log(`You have ${toJS(this.repos).length} 
            repository`)
    );
    reaction(
      () => this.showError.get(),
      () => console.log("something went wrong!!")
    );
  }
  fetchData() {
    fetch("https://api.github.com/users/t-kalra/repos")
      .then(res => res.json())
      .then(repos => {
        this.setWeatherData(repos);
      })
      .catch(() => this.setError());
  }
  @action.bound
  setWeatherData(data) {
    this.repos.push(data);
  }
@action.bound
  setError() {
    this.showError.set(true);
  }
}
const store = new githubReposList();
store.fetchData();

********************************************************************
Prints:
// You have 1 repository

runInAction:

Это функция полезности, эквивалентная действию (fn). Это используется для выполнения асинхронного кода внутри action ().

class cardActivity {
  operationState = observable.box("");
  activity = observable.array([], { deep: false });
constructor() {
    reaction(
      () => this.operationState.get(),
      () => console.log(this.operationState.get())
    );
    reaction(
      () => this.activity.length,
      len => console.log("No. of records available:", len)
    );
  }
  @action
  async fetchData() {
    this.operationState.set("pending");
    try {
      const response = await this.waitingForData();
      runInAction(() => {
        this.activity.push(response);
        this.operationState.set("completed");
      });
    } catch (e) {
      runInAction(() => {
        this.operationState.set("failed");
      });
    }
  }
  waitingForData() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve([
          {
            id: 1,
            name: "mobx"
          }
        ]);
      }, 2000);
    });
  }
}
const ccStore = new cardActivity();
ccStore.fetchData();
********************************************************************
Prints:
// pending 
// No. of records available: 1
// completed

Поток:

В некоторых случаях нам нужно использовать несколько ожиданий в функции и обновлять состояние при каждом успешном ожидании. Если мы будем использовать runInAction для каждой мутации состояния, это может сделать наш код сложным для управления. Чтобы было чище, течь

class FlowStore {
  @observable operationState = "";
  constructor() {
    reaction(
      () => this.operationState,
      data => {
        const d = new Date();
        console.log(
          data,
          `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`
        );
      }
    );
  }
  fetchData = flow(function*() {
    this.operationState = "pending";
    yield this.waitingForData(1000);
    this.operationState = "initialized";
    yield this.waitingForData(1500);
    this.operationState = "completed";
    yield this.waitingForData(2000);
    this.operationState = "reported";
  });
  waitingForData(delay) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(true);
      }, delay);
    });
  }
}
const store = new FlowStore();
store.fetchData();
********************************************************************
Prints:
// pending 18:41:3 
// initialized 18:41:4 
// completed 18:41:6 
// reported 18:41:8

Реакции:

Автозапуск: он принимает функцию в качестве входных данных и немедленно выполняет ее, а также отслеживает используемые переменные в переданной функции. Он повторно выполняется при изменении отслеживаемых наблюдаемых.

autorun( () => console.log(list.item) )

Когда: он наблюдает за предикатом до тех пор, пока он не станет истинным, и, как только это произойдет, выполняет функцию.

when( () => this.showModal, () => this.setHeaderDock() );

Реакция: требуется две функции, первая функция отслеживается и возвращает данные, которые используются в качестве входных данных второй функцией. Это не будет запускаться напрямую при создании, в отличие от автозапуска.

reaction( () => this.add, () => console.log() );

Наблюдатель: эту функцию / декоратор можно использовать для превращения компонентов ReactJS в реактивные компоненты. Любая наблюдаемая сущность, используемая в этом компоненте, при изменении вызывает повторный рендеринг компонента. Это часть пакета mobx-react.

var guestData = observable({
  guestNumber: Math.floor(100000 + Math.random() * 700000)
});
setInterval(() => {
  guestData.guestNumber = Math.floor(100000 + Math.random() * 
  700000);
}, 4000);
@observer
class GuestLogger extends React.Component {
  render() {
    return (
      <span>
        <b>Guest Information:</b> Mr.
           {this.props.guestData.guestNumber} just logged in.
      </span>
    );
  }
}
ReactDOM.render(<GuestLogger guestData={guestData} />, document.getElementById("root"));

********************************************************************
Prints:
// Guest Information: Mr. 556068 just logged in.

toJS ():

Наблюдаемый массив не является настоящим массивом JavaScript. вам нужно преобразовать это при передаче в другие библиотеки.

const items = observable.array([20]);
const plainArray = toJS(items);
console.log(plainArray);

Декоратор:

Это функция JavaScript, которая используется для изменения свойств / методов класса или самого класса. Прочтите, чтобы узнать больше Краткое руководство по декораторам ECMAScript

Украсить ():

Чистый подход к созданию наблюдаемого.
украшение (цель, объект-декоратор)

class UserInfo {
  name = "John";
  country = "Brazil";
  loginId = Math.floor(100000 + Math.random() * 700000);
  get userLabel() {
    return {
      __html: `<a href="/user/${this.loginId}"><b>${this.name}</b> 
               from ${this.country}</a>`
    };
  }
  updateCountry(country) {
    this.country = country;
  }
}
decorate(UserInfo, {
  name: observable,
  country: observable,
  userLabel: computed,
  updateCountry: action
});
const userInfo = new UserInfo();
console.log(userInfo.userLabel);
userInfo.updateCountry("Spain");
console.log(userInfo.userLabel);

********************************************************************
Prints:
// {__html: "<a href="/user/404093"><b>John</b> from Brazil</a>"}
// {__html: "<a href="/user/404093"><b>John</b> from Spain</a>"}

extendObservable:

Это позволяет вам добавлять дополнительные свойства во время выполнения и делать их наблюдаемыми.

class Modal {
  user = observable.object({
    name: "Tom",
    age: 30
  });
  constructor() {
    autorun(() => console.log(this.user, "autorun"));
    reaction(() => this.user,()=>console.log(this.user,`reaction
    `));
  }
  @action
  updateUserInfo(key, value) {
    this.user[key] = value;
  }
  setupReaction(key) {
    reaction(
      () => this.user[key],
      () => console.log(this.user[key], `- reaction on ${key}`)
    );
  }
}
const store = new Modal();
setTimeout(() => store.updateUserInfo("designation","DevOps"), 500);
extendObservable(store.user, { gitHubId: "Tomz" });
store.setupReaction("designation");
store.setupReaction("gitHubId");
setTimeout(() => store.updateUserInfo("designation", "QA"), 1000);
setTimeout(() => store.updateUserInfo("gitHubId", "Philips"), 1500);

********************************************************************
Prints:
// {name: "Tom", age: 30, gitHubId: "Tomz"} "autorun"
// NO REACTION FOR "designation" as neither this not part of 
   initialized observable nor added with extendObservable
// Philips - reaction on gitHubId

Принудительное использование действий:

Чтобы избежать прямого обновления наблюдаемой сущности.

Автоматическое обновление состояния приложения - это анти-шаблон - Мишель Вестстрат

импортировать {configure} из «mobx»;
configure ({enforceActions: true});

Меня зовут Тарун Калра. Я старший Frontend-разработчик в Publicis.Sapient.
Если вам понравилась эта статья, пожалуйста, хлопните n раз и поделитесь ею! Спасибо за ваше время.