it-roy-ru.com

React + Flux: получение начального состояния в магазине

Недавно мы переключились на React + Flux от Angular для создания довольно сложного бизнес-приложения.

Использование подхода с одним контейнерным компонентом, который передает все состояния в виде свойств по дереву компонентов, не является практическим способом разработки приложения для нас, так как приложение использует большие модальные страницы. Достаточное состояние передается модалам, чтобы они загружали свои данные в свои хранилища.

У меня проблема в том, что мне нужно получить начальное состояние (передаваемое как реквизит) в хранилище модального компонента. В этот пост хорошие ребята из Facebook говорят, что нормально использовать реквизит для начального состояния, когда синхронизация не является целью.

Вот как я сейчас получаю начальное состояние в своем магазине:

var ABC = React.createClass({
  ...  
  getInitialState: function() {
    return ABCStore.getInitialABCState(this.props.initialA);
  },
  ...

var ABCStore = Reflux.createStore({
  ...
  init: function() {
    _state = {
      a: null,
      b: 'B init',
      c: 'C init'
    };
  },

  getInitialABCState: function(initialA) {
    _state.a = initialA;
    return _state;
  },

  getABCState: function() {
    return _state;
  }
  ...

Я не уверен, что лучше всего делать это, или это анти-паттерн Flux?

13
Markus Coetzee

Мне кажется неправильным, что вы используете getInitialState() для изменения состояния вашего магазина. По крайней мере, вы должны делать это в componentWillMount .

Я бы вызвал действие в componentWillMount и обработчик хранилища обновил внутреннее состояние хранилища (это всегда должно происходить в потоке). Тогда обработчик изменений вашего компонента для хранилища может использовать любые данные, которые вы в данный момент называете «начальным состоянием».

12
Desmond Lee

Reflux.listenTo делает это, когда вы предоставляете третий аргумент, и Reflux.connect mixin factory (который использует Reflux.listenTo под капотом) обрабатывает это автоматически. Вот пример:

var Actions = Reflux.createActions({"doIt"});

var Store = Reflux.createStore({
    listenables: [Actions],
    init: function() {
        this.state = "I like to";
    },
    onDoIt: function() {
        this.state = "Do it";
        this.trigger(this.state);
    },
    getInitialState: function() {
        return this.state;
    }
});

var DoItButton = React.createClass({
    mixins: [Reflux.connect(Store, "label")],
    onClick: function() {
        Actions.doIt();
    },
    render: function() {
        return (<div onClick={this.onClick}>{this.state.label}</div>);
    }
});
4
Spoike

Как говорили другие авторы, лучше всего запустить действие в componentWillMount. В ES6 это обычно делается с помощью constructor.

Ниже приведен пример того, как сделать это с ES6:

(обратите внимание, что AuthorActions.initAuthors() зависит от реализации, это всего лишь пример. Это может получить начальное состояние из базы данных. Самое главное, что это действие должно передать полезную нагрузку с начальным состоянием диспетчеру)

var _authors = [];
var AuthorStoreInstance;

class AuthorStore extends EventEmitter {

    constructor(props) {
        super(props);
    }

    init() {
        AuthorActions.initAuthors();
        this.emitChange();
    }

    addChangeListener(cb) {
        this.on(CHANGE_EVENT, cb);
    }

    removeChangeListener(cb) {
        this.removeListener(CHANGE_EVENT, cb);
    }

    emitChange() {
        this.emit(CHANGE_EVENT);
    }

    getAllAuthors() {
        return _authors;
    }

    addAuthor(author) {
        _authors.Push(author);
        this.emitChange();
    }

    setAuthors(authors) {
        _authors = authors;
    }

};


AuthorStoreInstance = new AuthorStore();

Dispatcher.register((action) => {
    switch(action.actionType) {
        case ActionTypes.CREATE_AUTHOR:
            AuthorStoreInstance.addAuthor(action.author);
            break;
        case ActionTypes.INITIALIZE:
            AuthorStoreInstance.setAuthors(action.initialData.authors);
            break;

        default:
            //do nothing
    }
});

AuthorStoreInstance.init();

export default AuthorStoreInstance;

Обратите внимание, что функция init не является частью конструктора. Это связано с тем, что при создании authorStore обратный вызов для AuthorActions.initAuthors не был зарегистрирован диспетчером.

Инициализация должна произойти после того, как обратные вызовы были зарегистрированы диспетчером.

edit: для ясности, initAuthors может выглядеть примерно так:

initAuthors() {
    var authors = AuthorAPI.getAllAuthors();

    Dispatcher.dispatch({
        actionType: ActionTypes.INITIALIZE,
        initialData: {
            authors: authors
        }
    });
}
0
jacob