В этом посте мы рассмотрим основные концепции mobx на готовых примерах.
Введение. Mobx - это простая, хорошо протестированная библиотека с управлением состоянием, которая делает компоненты реактивными. Это на удивление быстро и последовательно. С Mobx вы можете создать несколько магазинов в вашем проекте.
Основные концепции Mobx:
- Наблюдаемые состояния
- Вычисленные значения на основе наблюдаемых состояний
- Реакции
Давайте начнем . . .
Наблюдаемый:
Функция observable () автоматически преобразует объект, массив или карту в наблюдаемую сущность.
- Использование по умолчанию: let list = observable ({item = 0})
- Объект: let list = observable.object ({item = 0})
- Массив: let list = observable.array ([])
- Карта: let list = observable.map (value);
- Примитивы, функции и экземпляры классов:
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 раз и поделитесь ею! Спасибо за ваше время.