多次通量状态更新后重新计算

时间:2018-01-03 15:05:12

标签: reactjs flux

我有一个使用在Typescript中编写的Flux的React应用程序。在页面上有两个引导下拉按钮,其中包含用户可以选择的值以影响查询结果集。

使用正常的Flux Actions和Stores,我调用几个api调用来检索绑定到状态的下拉按钮列表的json中的值。

我正在尝试对计算函数进行初始触发,一旦调用完成,需要来自两个下拉列表的值,但由于状态设置是异步的,我不知道何时触发它?

我已尝试过我的组件上的状态设置功能,但我总是被告知在调度时无法调度(即通过动作/存储方法调用我的calc函数)。

如何在调用函数之前等待两个调用完成?

componentWillMount() {
    AvailabilityTimePresetsStore.addChangeListener(this._onChange_availabilityTimePresetsStore);
    AvailabilityTimePresetActions.getTimePresets();
}

_onChange_availabilityTimePresetsStore = () => {
        let timePresets = this._getStateFromAvailabilityTimePresetsStore().presets;
        // set initial value
        let currentPreset = this.state.selectedTimePreset;
        this.setState({ timePresetOptions: timePresets, selectedTimePreset: currentPreset == null ? timePresets[0] : currentPreset });
    }

_getStateFromAvailabilityTimePresetsStore() {
    // retrieve the state from the store
    return AvailabilityTimePresetsStore.getState();
}

从我的TimePresetsStore触发emitChange时调用上面的内容。我检查是否没有先前选择的预设,如果没有,则将selectedTimePreset设置为第一个(它是初始加载)。此时我想触发我的recalc()函数以从查询中获取结果。但是,如果我在这里调用它或作为回调ater setState我得到调度程序错误。我也无法告诉这里是否填充了另一个下拉列表并以相同的方式设置,我需要将所选值传递给查询。

1 个答案:

答案 0 :(得分:1)

好吧,我猜你实施Flux的方式似乎并不是最好的。 让我举一个例子:

//ModelActions.js

export default class ModelActions {
  static actionOne(params) {
    Dispatcher.handleViewAction({
      actionType: 'ACTION_ONE_STARTING'
    });

    fetch('uri', ...params)
      .then(resp => {
        Dispatcher.handleViewAction({
          actionType: 'ACTION_ONE_SUCCESS',
          payload: resp
        });

        //I could also dispatch more actions here, like:
        Dispatcher.handleViewAction({
          actionType: 'ACTION_TWO'
        });
      })
      .catch((error) => {
        Dispatcher.handleViewAction({
          actionType: 'ACTION_ONE_ERROR',
          payload: error.message
        });
      });
  }
}

//ModelStore.js
import { EventEmitter } from 'events';
import assign from 'object-assign';
import YourDispatcher from '../dispatcher/YourDispatcher';

let ModelStore = assign({}, EventEmitter.prototype, {
  emitChange() {
    this.emit('change');
  },

  addChangeListener(callback) {
    return this.on('change', callback);
  },

  removeChangeListener(callback) {
    this.removeListener('change', callback);
  },

  actionOneStarting() {
    return _isActionOneStarting;
  },

  actionOneSuccess() {
    return _isActionOneSuccess;
  },

  getActionOneError() {
    return _actionOneError;
  },

  getActionOnePayload() {
    return _actionOnePayload;
  }
});

ModelStore.dispatchToken = YourDispatcher.register((action) => {
  switch (action.actionType) {
    case 'ACTION_ONE_STARTING':
      _isActionOneStarting = true;
      _isActionOneSuccess = false;
      ModelStore.emitChange();
      break;

    case 'ACTION_ONE_SUCCESS':
      _isActionOneStarting = false;
      _isActionOneSuccess = true;
      _actionOnePayload = action.payload;
      ModelStore.emitChange();
      break;

    case 'ACTION_ONE_ERROR':
      _isActionOneStarting = false;
      _isActionOneSuccess = false;
      _actionOnePayload = null;
      _actionOneError = action.error
      ModelStore.emitChange();
      break;

    default:
      break;
  }
});

module.exports = ModelStore;

//YourComponent.js
import React, { Component } from 'react';

import ModelActions from '../../actions/ModelActions';
import ModelStore from '../../stores/ModelStore';

export default class YourComponent extends Component {
  componentDidMount() {
    ModelStore.addChangeListener(this._onStoreChange);
    ModelActions.actionOne();
  }

  componentWillUnmount() {
    ModelStore.removeChangeListener(this._onStoreChange);
  }

  render() {
    return (

    );
  }

  _onStoreChange() {
    this.setState({
      starting: ModelStore.actionOneStarting(),
      success: ModelStore.actionOneSuccess(),
      error: ModelStore.getActionOneError(),
      payload: ModelStore.getActionOnePayload()
    }, () => {
      //Here the component will react as soon as an action arrives
      //DOes not matter how many actions are dispatched
    });
  }
}

总结一下,您可以根据需要调度任意数量的操作,每个对此类操作感兴趣的商店必须有一个swith来捕获该操作,对更改做出反应并发出更改,这同样适用于组件,开始听所需的所有商店。

现在让我们来看看如何使用Redux:

//YourComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';

import ModelActions from '../actions/ModelActions';

export default class YourComponent extends Component {
  componentDidMount() {
    this.props.actionOne();
  }

  render() {
    return (
        //You dont need to change the state any more, just render based on props
    );
  }
}

const mapStateToProps = state => {
  return {
    starting: state.model.starting,
    success: state.model.success,
    error: state.model.error,
    payload: state.model.payload,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    actionOne: () => ModelActions.actionOne()(dispatch) //There is a tiny trick here, look for [redux-thunk](https://github.com/gaearon/redux-thunk)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(YourComponent);

//ModelActions.js

export function ModelActions(params) {
  return dispatch => {
    dispatch({type: 'ACTION_ONE_STARTING'});

    fetch('uri', ...params)
      .then(resp => {
        dispatch({type: 'ACTION_ONE_SUCCESS', payload: resp});
      })
      .catch((error) => {
        dispatch({type: 'ACTION_ONE_ERROR', payload: error.message});
      });
  }
}

//model.js
const initialState = {
    starting: false,
    success: false,
    error: null,
    payload: null,
};

export default function model(state = initialState, action = {}) {
  switch (action.type) {
    case 'ACTION_ONE_STARTING':
      return {
        ...state,
        starting: true,
        success: false
      }
    case 'ACTION_ONE_SUCCESS':
      return {
        ...state,
        starting: false,
        success: true,
        payload: action.payload
      }
    case 'ACTION_ONE_ERROR':
      return {
        starting: false,
        success: false,
        error: action.payload
      }
    default:
      return state;
  }
}

在redux中,存储被reducers替换,组件和动作非常相似。纯Flux和redux之间的主要区别之一是redux通过props注入所有需要呈现的信息组件,确保应用程序的整个状态存在于reducers而不是生活在组件中。

你会发现关于redux和纯Flux之间差异的一个很好的解释(我说纯粹的Flux因为redux是一种非Flux实现)here

有关redux的更多信息,请参阅the Official Site