Как мне реализовать ленивое поведение с MobX?

Следующий код предназначен для печати перевернутого списка пользователей при добавлении нового пользователя, но он не работает. Автозапуск слушает ленивую вычисляемую переменную (_userArrayRev), но как включить пересчет этой переменной? Автозапуск выполняется только один раз, в то время как я ожидаю, что он будет запущен три раза.

И почему MobX позволяет мне изменять наблюдаемую переменную userArray в AddUser(), когда для принудительных действий (useStrict) установлено значение true?

import { useStrict, configure, autorun } from 'mobx';
import { observable, action, computed } from 'mobx';


configure({ enforceActions: true });


class Test {
    @observable _userArray = [];
    @observable _userArrayRev = undefined;
    userCount = 0;

    addUser() {
        console.log("Adduser");
        this._userArray.push('user' + this.userCount);
        this.invalidateCache();
    }

//  returns reversed array
    getUsersArrayRev() {
        if (this._userArrayRev == undefined) {
//          console.log("recalculating userArray");
            // TODO: should be reversed
            this._userArrayRev = this._userArray;
        }
        return this._userArrayRev;
    }

    invalidateCache() {
        this._usersArrayRev = undefined;
    }

}

var t = new Test();

autorun(function test () {
    console.log("users: ", t.getUsersArrayRev());
});
t.addUser();
t.addUser();

person cdarwin    schedule 03.04.2018    source источник


Ответы (1)


Я рекомендую вам использовать computed вместо autorun. computed больше подходит в том случае, когда вы хотите создать ленивую переменную только для чтения на основе observable объектов.

Примечание. Я использую slice() для возврата обычного массива. Наблюдаемый массив — это объект, а не массив, будьте осторожны.

import React from 'react';
import { render } from 'react-dom';
import { observer } from 'mobx-react';
import { observable, action, computed } from 'mobx';

class Test {
  @observable _userArray = [];

  @computed get userCount() {
    return this._userArray.length;
  }

  @computed get usersArrayRev() {
    return this._userArray.slice().reverse();
  }

  @action
  addUser() {
    console.log("Adduser");
    const id = this.userCount + 1;
    this._userArray.push(`user${id}`);
    console.log("Reversed users: ", this.usersArrayRev);
  }
}

@observer
class App extends React.Component {
  t = new Test();

  render() {
    return (
      <div>
        {this.t.usersArrayRev.map(user => <div key={user}>{user}</div>)}
        <button onClick={() => { this.t.addUser(); }}>Add user</button>
      </div>
    );
  }
}

Демонстрация кода здесь:

  Редактировать вычисленный mobx

person FisNaN    schedule 03.04.2018
comment
Я не вижу mobx, используемого в приведенном примере, я ошибаюсь?... и ваш ответ хорош, но не делает именно то, что я хочу, то есть автоматически печатает обратный список пользователей каждый раз, когда вызывается adduser (без вставки код для печати внутри addUser, но снаружи, так как в реальном мире у меня есть компонент React, который должен быть повторно отображен при добавлении пользователя) - person cdarwin; 04.04.2018
comment
@cdarwin Я не уверен, что вы подразумеваете под печатью. Если вы используете t.usersArrayRev.map(user=> <User user={user} />), ваш компонент будет автоматически перерисовываться каждый раз, когда вы добавляете нового пользователя. - person FisNaN; 04.04.2018
comment
Под печатью я подразумеваю вывод на консоль обратных пользователей. Однако вы должны быть правы насчет ререндеринга React. Сейчас я ложусь спать, завтра перепроверю ваш ответ. - person cdarwin; 04.04.2018
comment
Я вижу, что ваш код работает и без @computed (даже в строгом режиме), я все еще не понимаю, когда нужно вычислить - person cdarwin; 04.04.2018
comment
Ключевое слово @cdarwin computed создает ленивый объект на основе наблюдаемого значения. Без computed это по-прежнему функция получения, но это может вызвать проблемы с асинхронностью. Подумайте об использовании значения состояния сразу после setState, иногда вы можете получить предыдущее значение. - person FisNaN; 04.04.2018