Руководство для начинающих Mobx React для начинающих

MobX
Руководство для начинающих Mobx React для начинающих

государство государство

UI = fn(state)

Приведенная выше формула показывает, что при одном и том жеstateусловие,fnвсегда будет генерировать последовательныйUI.

В мире React нам также нужно добавитьpropsТолько полное:

VirtualDOM = fn(props, state)

Action => state => UI

Из рисунка видно, что в пользовательском интерфейсе мы можем выполнять интерфейсные операции (нажимать кнопки, набирать ввод с клавиатуры и т. д.), эти интерфейсные операции называютсяaction. Важным моментом здесь является то, что поток данныхUI => action => state,UIНе изменять напрямуюstate, а путем отправкиactionтем самым изменяяstate.

Польза от этого очевидна, т.UIСлой отвечает только за синхронный рендеринг интерфейса.

когдаstateПосле изменения он уведомит всех своихobserversнаблюдатель.UIПросто один из самых важных наблюдателей, обычно есть и другие.

side effect

Дополнительные наблюдатели уведомлены о том, что мы вызываем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функция илиclassclass, он не будет работать и даже выдаст исключение.

Для этих специальных типов 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;
    }
}