государство государство
UI = fn(state)
Приведенная выше формула показывает, что при одном и том жеstate
условие,fn
всегда будет генерировать последовательныйUI
.
В мире React нам также нужно добавитьprops
Только полное:
VirtualDOM = fn(props, state)
Из рисунка видно, что в пользовательском интерфейсе мы можем выполнять интерфейсные операции (нажимать кнопки, набирать ввод с клавиатуры и т. д.), эти интерфейсные операции называютсяaction
. Важным моментом здесь является то, что поток данныхUI => action => state
,UI
Не изменять напрямуюstate
, а путем отправкиaction
тем самым изменяяstate
.
Польза от этого очевидна, т.UI
Слой отвечает только за синхронный рендеринг интерфейса.
когдаstate
После изменения он уведомит всех своихobservers
наблюдатель.UI
Просто один из самых важных наблюдателей, обычно есть и другие.
Дополнительные наблюдатели уведомлены о том, что мы вызываемside effects
, выполнятьside effect
Дальше все пойдет само собойaction
отправки для обновленияstate
,с участиемstate
Есть существенная разница.
Основные концепции MobX
import { observable } from 'mobx';
let cart = observable({
itemCount: 0,
modified: new Date()
});
observable
это наблюдаемое состояние, которое является реактивным.
Наблюдаемое объявлено, а затем его нужно объявитьobserver
Наблюдатели имеют смысл.
import { observable, autorun } from 'mobx';
autorun(() => {
console.log(`The Cart contains ${cart.itemCount} item(s).`);
}); // => 控制台输出: The Cart containers 0 item(s)
cart.itemCount++; // => 控制台输出: The Cart containers 1 item(s)
autorun
является одним из наблюдателей, он будет автоматически наблюдать за функцией вobservable
переменная, если переменная в функции изменится, она выполнит функцию один раз. (В частности, она будет выполняться сразу после регистрации функции, вне зависимости от того, изменилась ли переменная. Отсюда вышеизложенноеitemCount
изменен один разautorun
Результат выполнения 2 раза)
похожий наredux
идеи, непосредственно изменитьstate
является греховным и в конечном итоге испортит программу. существуетmobx
Внутри тоже сверхуcart.itemCount++
Для этой операции нам нужно поместить его вaction
входить.
import { observable, autorun, action } from 'mobx';
const incrementCount = action(() => {
cart.itemCount++;
})
incrementCount();
существуетmobx
внутри,side effects
побочные эффекты, также называемыеreactions
.reaction
а такжеaction
Разница в следующем:action
для измененияstate
, покаreaction
являетсяstate
После того, как изменение должно быть реализовано.
action => state => reaction
Действия изменяют состояния, а состояния вызывают реакции.
Observables , Actions , Reactions
observable()
будуobject
,array
илиmap
Преобразованный в наблюдаемый объект Наблюдаемый объект. А для основных типов JavaScript (number
, string
, boolean
, null
, undefined
),function
функция илиclass
class, он не будет работать и даже выдаст исключение.
Для этих специальных типов MobX предоставляетobservable.box()
API, использование выглядит следующим образом:
const count = observable.box(20);
console.log(`Count is ${count.get()}`); // get()
count.set(25); // set()
дляobservable
Конкретные сценарии использования API следующие:
тип данных | API |
---|---|
object | observable.object({}) |
arrays | observable.array([]) |
maps | observable.map(value) |
primitives, functions, class-instances | observable.box(value) |
Есть похожие Vuex of mobxcomputed
функция, в MobX мы называем этоderivations
Производное состояние. Использовать его легко, просто объявите объектget
Атрибуты:
const cart = observable.object({
items: [],
modified: new Date(),
get description() {
switch (this.items.length) {
case 0:
return 'no items in the cart';
default:
return `${this.items.length} items in the cart`;
}
}
})
Выше мы все используем синтаксис es5, в es6 мы можем использовать наш MobX в виде декораторов:
class Cart {
@observable.shallow items = []; // => observable.array([], { deep: false })
@observable modified = new Date();
@computed get description() {
switch (this.items.length) {
case 0:
return 'no items in the cart';
default:
return `${this.items.length} items in the cart`;
}
}
@action
addItem = () => {
this.items.push('new one');
}
}
MobX имеет 3 типаreactions
:autorun()
, reaction()
, when()
.
autorun()
import { observable, action, autorun } from 'mobx';
class Cart {
@observable modified = new Date();
@observable.shallow items = [];
constructor() {
this.cancelAutorun = autorun(() => {
console.log(`Items in Cart: ${this.items.length}`); // 1. 控制台输出: Items in Cart: 0
});
}
@action
addItem(name, quantity) {
this.items.push({ name, quantity });
this.modified = new Date();
}
}
const cart = new Cart();
cart.addItem('Power Cable', 1); // 2. 控制台输出: Items in Cart: 1
cart.addItem('Shoes', 1); // 3. 控制台输出: Items in Cart: 2
cart.cancelAutorun();
cart.addItem('T Shirt', 1); // 控制台不输出
autorun(effect-function): disposer-function
effect-function: (data) => {}
Доступна сautorun()
Видно, что подпись выполняетсяautorun()
После этого верните выходeffect-function
изdisposer-function
функция, эта функция возврата используется для остановкиautorun()
мониторинг, аналогичныйclearTimer(timer)
эффект.
reaction()
reaction(tracker-function, effect-function): disposer-function
tracker-function: () => data, effect-function: (data) => {}
reaction()
Сравниватьautorun()
еще одинtracker-function
функция, эта функция используется для контроляstate
генерировать вывод вeffect-function
изdata
. только когда этоdata
при смене,effect-function
будет запущен для выполнения.
import { observable, action, reaction, toJS } from 'mobx';
class ITOffice {
@observable members = []
constructor() {
reaction(() => {
const femaleMember = this.members.find(x => x.sex === 'female');
return femaleMember;
}, femaleMember => {
console.log('Welcome new Member !!!')
})
}
@action addMember = (member) => {
this.members.push(member)
}
}
const itoffice = new ITOffice();
itoffice.addMember({
name: 'salon lee',
sex: 'male'
});
itoffice.addMember({
name: 'little ming',
sex: 'male'
});
itoffice.addMember({
name: 'lady gaga',
sex: 'female'
}); // 1. 控制台输出: Welcome new Member !!!
Офис выше,reaction()
Новые сотрудники отслеживаются на предмет присоединения, но приветственные песнопения слышны только в том случае, если новый сотрудник женского пола. Это дифференцированное обращение контролируетсяtracker-function
осуществленный.
when()
when(predicate-function, effect-function): disposer-function
predicate-function: () => boolean, effect-function: () => {}
when()
а такжеreaction()
Точно так же существует функция предварительного определения, ноwhen()
возвращает логическое значениеtrue/false
. только тогда, когдаpredicate-function
вернутьtrue
час,effect-function
Также казнены иeffect-function
будет выполняться только один раз. то естьwhen()
является одноразовым побочным эффектом, когда условие истинно и возникает побочный эффект,when()
Он автоматически выйдет из строя, что эквивалентно вызову его самостоятельно.disposer-function
функция.
when()
Есть еще один способ записи, который заключается в использованииawait when()
и пройти только первыйpredicate-function
параметр.
async () {
await when(predicate-function);
effect-function();
} // <= when(predicate-function, effect-function)
MobX React
существуетReact
используется вmobx
, нам нужно установитьmobx-react
библиотека.
npm install mobx-react --save
и использоватьobserver
Подключите компоненты реакции и состояние mobx.
Сначала создайте нашу корзину:
// CartStore.js
import { observer } from "mobx-react";
export default class Cart {
@observer modified = new Date();
@observer.shallow items = [];
@action
addItem = (name, quantity) {
while (quantity > 0) {
this.items.push(name)
quantity--;
}
this.modified = new Date();
}
}
Затем передайте статус корзины черезProvider
Вставить в контекст:
// index.js
import { Provider } from 'mobx-react';
import store from './CartStore'
ReactDOM.render(
<Provider store={new store()}>
<App />
</Provider>,
document.getElementById('root')
);
Затем перейдите в другие файлы компонентовinject
Будуstore
вводить вprops
:
// app.js
import React from 'react';
import './App.css';
import { inject, observer } from 'mobx-react';
@inject('store')
@observer
class App extends React.Component {
render() {
const { store } = this.props;
return (
<React.Fragment>
{store.items && store.items.map((item, idx) => {
return <p key={item + idx}>{item + idx}</p>
})}
<button onClick={() => store.addItem('shoes', 2)}>添加2双鞋子</button>
<button onClick={() => store.addItem('tshirt', 1)}>添加1件衬衫</button>
</React.Fragment>
);
}
}
export default App;
дизайн магазина
Поздравляем с просмотром последней главы Руководства для начинающих, эта статья не охватывает многого.MobX
Высокоуровневый API и внутренние принципы .. Заголовок называется "Руководство для начинающих". Почему мы используем такие сложные вещи, чтобы пугать людей, и после того, как вы проверили вышеуказанный контент, вы определенно справитесь с подавляющим большинством ежедневных жизнь Большинство сценариев развития. Так что не паникуйте, смотрите здесь, вы тоже новичокmobx
. поздравления.
Наконец показано здесь, когда вы используетеmobx
В качестве вашего решения по управлению состоянием, как вы должны спроектировать свойstore
. На самом деле это больше склонность к индивидуальному или командному стилю и мышлению на уровне плюсов и минусов.
Здесь нет стандартного ответа, просто для справки.
Шаг 1: Декларацияstate
class Hero {
@observable name = 'Hero'; // 名字
@observable blood = 100; // 血量
@observable magic = 80; // 魔法值
@observable level = 1; // 等级
constructor(name) {
this.name = name; // 初始化英雄名字
}
}
Шаг 2: По вашему ключуstate
полученныйcomputed
class Hero {
@observable name = 'Hero';
@observable blood = 100;
@observable magic = 80;
@observable level = 1;
@computed
get isLowHP() { // 是否低血量
return this.blood < 25;
}
@computed
get isLowMC() { // 是否低魔法值
return this.magic < 10;
}
@computed
get fightLevel() { // 战斗力
return this.blood * 0.8 + this.magic * 0.2 / this.level
}
constructor(name) {
this.name = name;
}
}
Шаг 3: Декларацияaction
class Hero {
@observable name = 'Hero';
@observable blood = 100;
@observable magic = 80;
@observable level = 1;
@computed
get isLowHP() {
return this.blood < 25;
}
@computed
get isLowMC() {
return this.magic < 10;
}
@computed
get fightLevel() {
return this.blood * 0.8 + this.magic * 0.2 / this.level
}
@action.bound
beAttack(num) { // 被攻击
this.blood -= num;
}
@action.bound
releaseMagic(num) { // 释放魔法
this.magic -= num;
}
@action.bound
takePill() { // 吃药丸
this.blood += 50;
this.magic += 25;
}
constructor(name) {
this.name = name;
}
}