我有一个使用在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我得到调度程序错误。我也无法告诉这里是否填充了另一个下拉列表并以相同的方式设置,我需要将所选值传递给查询。
答案 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