Вызов другого действия на основе результатов другого действия в React JS

У меня есть PageComponent, он содержит следующие реагирующие компоненты: -

  1. Добавить форму элемента. [Компонент формы]
  2. Постраничный список предметов. [КомпонентСпискаПользователей]

Когда пользователь нажимает «Добавить элемент» в форме «Добавить элемент». Действие вызывается с помощью ActionCreator, а затем оно вызывает сервер API вместе с обратным вызовом Success/Failure.

//Calling action action creator from the component
ActionCreator.addUser({email:email, dept_code:dept});


//Action Creator calling action using dispatcher.
addUser: function(userObj){
  ApiClient.addUser(
    userObj,
    function(response){
       AppDispatcher.handleServerAction({actionType:ActionTypes.ADD_USER_SUCCESS, response: response});
    }.bind(this),
    function(error){
       AppDispatcher.handleServerAction({actionType:ActionTypes.ADD_USER_FAIL, error: error});
    }.bind(this)
  );
}

Когда вызывается обратный вызов успеха/неудачи, он отправляет действие, например, ADD_USERS_SUCCESS.

Я настроил PageStore для прослушивания этого действия и информирования пользователя об отправке формы.

  dispatcherIndex: AppDispatcher.register(function(payload) {
      var action = payload.action;
      switch(action.actionType){
         case ActionTypes.LOAD_USERS_SUCCESS:
            persistStoreData(false, null, action.usersObj);
            break;
         case ActionTypes.LOAD_USERS_FAIL:
            persistStoreData(false, payload.action.error, {});
            break;
         case ActionTypes.ADD_USER_SUCCESS:
            updateAddFormData(false, "Added", "success", []);
            break;
         case ActionTypes.ADD_USER_FAIL:
            updateAddFormData(true, "Add Failed! Click to retry..", "danger", payload.action.error);
            break;
         default:
            return true;
      }
      UsersStore.emitChange();
      return true; // No errors. Needed by promise in Flux Dispatcher.
 })

Проблема в том, как мне обновить свой UserListComponent, если запускается действие ADD_USERS_SUCCESS.

У меня есть следующее решение: -

  1. Запуск действия (например, LOAD_USERS), в котором пользователи будут перечислены в моем методе рендеринга, например, если у меня есть флаг в моем состоянии, например {reloadUserTable: true}?

  2. Обновление состояния в методе рендеринга, но в соответствии с документами Facebook состояние обновления в методе рендеринга — это AntiPattern.


person techgyani    schedule 23.02.2015    source источник


Ответы (4)


Вы можете поддерживать состояния внутри PageComponent и позволить его «детям» (UserListComponent) получать к нему доступ, используя свойство props.

var PageComponent = React.createClass({
         getUserState: function() {
            return {
                allUsers: UsersStore.getAllUsers()
            }
         },
         getInitialState: function() {
            return getUserState();
         },
         /* at ADD_USERS_SUCCESS */
         onChange: function() {
            this.setState(getUserState());
         },
         render: function() {
            <FormComponent />
            {/* inside UserListComponent, access all users using this.props.users */}
            <UserListComponent users={this.state.allUsers} />
         }});
person Oppo    schedule 23.02.2015
comment
Если бы я мог сделать предложение, некоторое описание того, что делает ваш код, могло бы помочь спрашивающему. - person Rüdiger Herrmann; 23.02.2015
comment
Является ли вызов хранилища из представления в порядке в событии onChange или я должен вызвать действие, как показано ниже: onChange: function() { ActionCreator.loadUsers({page:1}); ; }, - person techgyani; 24.02.2015
comment
После того, как хранилища обновляются в ответ на действие, они генерируют событие изменения. Представления прослушивают события изменений, извлекают новые данные из хранилищ. Другими словами, совершенно нормально вызывать Store из представлений. Нет необходимости вызывать другое действие onChange. - person Oppo; 24.02.2015
comment
Спасибо, я понимаю, что вы говорите. Мне нужно инициировать действие для получения данных списка пользователей с веб-сервера, потому что я где-то читал, что хранилище не должно напрямую общаться с веб-сервисами. - person techgyani; 24.02.2015
comment
Да, веб-сервисы в случае успеха/неудачи должны вызывать действие с данными, которые передаются в магазины через диспетчер, а затем магазины могут обновлять себя новыми данными и выдавать изменения. - person Oppo; 24.02.2015
comment
Я с тобой согласен. Я написал полный ответ на основе этого обсуждения ниже. stackoverflow.com/a/28689797/1922960 - person techgyani; 24.02.2015

Если ваш PageStore содержит ссылку на страницы, которые связаны с компонентом UserListComponent, то в случае успеха вы можете добавить эту ссылку и внести изменения.

person pra    schedule 23.02.2015
comment
Спасибо, проблема в том, что этот подход потребует много работы, связанной с поддержкой нумерации страниц на стороне клиента. Итак, я хочу получить список пользователей с сервера. - person techgyani; 24.02.2015
comment
Если вы всегда собираетесь загружать пользователей (вместе с нумерацией страниц), то почему бы не запустить событие загрузки вместе с событием ADD_USER_SUCCESS? - person pra; 24.02.2015

просто предоставьте метод дочернему компоненту для вызова родителем - http://facebook.github.io/react/tips/expose-component-functions.html

person Alex    schedule 23.02.2015
comment
Пожалуйста, уточните свой ответ, а не просто ссылайтесь на документацию. Как этот документ относится к вопросу? - person mmmmmpie; 23.02.2015

Мое рабочее решение для решения этой проблемы, как показано ниже. Мне помог обратный вызов _onChange в решении, предоставленном @oppo.

Что я сделал для решения этой проблемы: 1. Когда вызывается действие добавления пользователя, я устанавливаю флаг в своем магазине, например {reloadUsers:true} действие для загрузки данных с сервера API.

Ниже находится магазин

'use strict';
var AppDispatcher = require('../dispatcher/AppDispatcher');
var Constants = require('../constants/Constants');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');

var ActionTypes = Constants.ActionTypes;
var CHANGE_EVENT = 'change';
var _state = {
               loading: false,
               error : null,
               users: {},
               isAdded: true,
               addFormErrors: [],
               reloadUsers: false //Whether to reload user table or not
            };
//Stores data recieved from server on page load
function persistStoreData(loading, error, response) {
   _state.loading = loading;
   _state.error = error;
   _state.users = response;
   _state.reloadUsers = false;
}

//Updates data recieved from server if data saved
function updateAddFormData(enableSave){
   _state.enableAddButton = enableSave;
   if(!_state.enableAddButton){
      _state.isAdded = true;
      _state.reloadUsers = true;
   }
   else{
      _state.isAdded = false;
   }
}

var UsersStore = assign({}, EventEmitter.prototype, {
   getState: function(){
      return _state;
   },

   getUsers: function(){
      return this._users;
   },

   emitChange: function() {
    //console.log('store change event');
    this.emit(CHANGE_EVENT);
   },

   /**
   * @param {function} callback
   */
  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  /**
   * @param {function} callback
   */
  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  dispatcherIndex: AppDispatcher.register(function(payload) {
      var action = payload.action;
      switch(action.actionType){
         case ActionTypes.LOAD_USERS_SUCCESS:
            persistStoreData(false, null, action.usersObj);
            break;
         case ActionTypes.LOAD_USERS_FAIL:
            persistStoreData(false, payload.action.error, {});
            break;
         case ActionTypes.ADD_USER_SUCCESS:
            updateAddFormData(false, "Added", "success", []);
            break;
         case ActionTypes.ADD_USER_FAIL:
            updateAddFormData(true, payload.action.error);
            break;
         default:
            return true;
      }
      UsersStore.emitChange();
      return true; // No errors. Needed by promise in Flux Dispatcher.
 })

});
module.exports = UsersStore;

Далее идет диспетчер

'use strict';
var Constants = require('../constants/Constants');
var Dispatcher = require('flux').Dispatcher;
var assign = require('object-assign');

var PayloadSources = Constants.PayloadSources;

var AppDispatcher = assign(new Dispatcher(), {

  /**
   * @param {object} action The details of the action, including the action's
   * type and additional data coming from the server.
   */
  handleServerAction: function(action) {
    var payload = {
      source: PayloadSources.SERVER_ACTION,
      action: action
    };
    this.dispatch(payload);
  }

});

module.exports = AppDispatcher;

Ниже приведены мои константы

var keyMirror = require('keymirror');

module.exports = {
   ActionTypes: keyMirror({
      ADD_USER_SUCCESS: null,
      ADD_USER_FAIL: null,

      LOAD_USERS: null,
      LOAD_USERS_SUCCESS: null,
      LOAD_USERS_FAIL: null,
   }),
   PayloadSources: keyMirror({
      SERVER_ACTION: null,
   })
};

Ниже приведен создатель действия.

'use strict';
var AppDispatcher = require('../dispatcher/AppDispatcher');
var Constants = require('../constants/Constants');
var ApiClient = require('../clients/ApiClient');
var ActionTypes = Constants.ActionTypes;

var ActionCreator = {

  loadUsers: function(){
     ApiClient.getUsers(function(usersObj) {
       AppDispatcher.handleServerAction({actionType:ActionTypes.LOAD_USERS_SUCCESS, usersObj: usersObj});
     }.bind(this), function(error) {
      AppDispatcher.handleServerAction({actionType:ActionTypes.LOAD_USERS_FAIL, error: error});
     }.bind(this));
   }

    addUser: function(userObj){
      ApiClient.addUser(
        userObj,
        function(response){
           AppDispatcher.handleServerAction({actionType:ActionTypes.ADD_USER_SUCCESS, response: response});
        }.bind(this),
        function(error){
           AppDispatcher.handleServerAction({actionType:ActionTypes.ADD_USER_FAIL, error: error});
        }.bind(this)
      );
    }
};
module.exports = ActionCreator;

Ниже приведен основной компонент представления (только важные части).

'use strict';
var React = require('react');
var ActionCreator = require('../actions/ActionCreator');
var UsersStore = require('../stores/UsersStore');
var UsersTable = require('./UsersTable.jsx');
var UserAddForm = require('./UserAddForm.jsx');
var ManageUsers = React.createClass({

  getInitialState: function() {
      return UsersStore.getState();
  },

  componentDidMount: function() {
      ActionCreator.loadUsers();//Invoking Action, loading initial data
      UsersStore.addChangeListener(this._onChange);
  },

  componentWillUnmount: function() {
     UsersStore.removeChangeListener(this._onChange);
  },

  _onChange: function() {
     var state = UsersStore.getState();
     if(state.reloadUsers === true){
       //Only reload users if state has this variable set
       ActionCreator.loadUsers();
     }
     else{
       this.setState(state);
     }
  },
  render: function(){
    //Rendering logic would go here, below is just a prototype
    return (
       <div>
       <UserAddForm onSubmit={handleFormSubmit} />
       <UsersTable/>
       </div>
    );
  }
});

module.exports = ManageUsers;

Ниже приведен API-клиент

'use strict';
var $ = require('jquery');
var ApiClient = {

   getUsers : function(success, failure){
      $.ajax({
         url : '/api/get-users',
         dataType: 'json',
         success : function(data){
            success(data);
         },
         error : function(jqXHR, textStatus, errorThrown){
            failure(errorThrown);
         }
      });
   },

   addUser : function(data, success, failure){
      $.ajax({
         url : '/api/add-user',
         dataType: 'json',
         type: 'post',
         data: 'data='+JSON.stringify(data),
         success : function(data){
            success(data);
         },
         error : function(jqXHR){
            var errorObj = {};
            try{
              errorObj = JSON.parse(jqXHR.responseText);
            }
            catch(e){
              errorObj['main'] = "An error occured";
            }
            failure(errorObj);
         }
      });
   }
};
module.exports = ApiClient;
person techgyani    schedule 24.02.2015