是否可以在特定路由上调用异步redux操作(称为thunk),并且在响应成功或失败之前不执行转换?
用例
我们需要从服务器加载数据并使用初始值填充表单.在从服务器获取数据之前,这些初始值不存在.
像这样的一些语法会很棒:
<Route path="/myForm" component={App} async={dispatch(loadInitialFormValues(formId))}>
回答在响应成功或失败之前阻止转换到新路由的原始问题:
因为你正在使用redux thunk,你可以在动作创建器中成功或失败触发重定向.我不知道你的具体动作/动作创建者是什么样的,但这样的事情可能有效:
import { browserHistory } from 'react-router' export function loadInitialFormValues(formId) { return function(dispatch) { // hit the API with some function and return a promise: loadInitialValuesReturnPromise(formId) .then(response => { // If request is good update state with fetched data dispatch({ type: UPDATE_FORM_STATE,payload: response }); // - redirect to the your form browserHistory.push('/myForm'); }) .catch(() => { // If request is bad... // do whatever you want here,or redirect browserHistory.push('/myForm') }); } }
跟进.在输入路径/组件的componentWillMount并显示微调器时加载数据的常见模式:
来自异步操作http://redux.js.org/docs/advanced/AsyncActions.html的redux文档
- An action informing the reducers that the request began.
The reducers may handle this action by toggling an isFetching flag in
the state. This way the UI knows it’s time to show a spinner.
- An action informing the reducers that the request finished successfully.
The reducers may handle this action by merging the new data into the
state they manage and resetting isFetching. The UI would hide the
spinner,and display the fetched data.
- An action informing the reducers that the request Failed.
The reducers may handle this action by resetting isFetching.
Additionally,some reducers may want to store the error message so the
UI can display it.
我使用您的情况作为粗略的指导方针,遵循下面的这种一般模式.您不必使用承诺
// action creator: export function fetchFormData(formId) { return dispatch => { // an action to signal the beginning of your request // this is what eventually triggers the displaying of the spinner dispatch({ type: FETCH_FORM_DATA_REQUEST }) // (axios is just a promise based HTTP library) axios.get(`/formdata/${formId}`) .then(formData => { // on successful fetch,update your state with the new form data // you can also turn these into their own action creators and dispatch the invoked function instead dispatch({ type: actions.FETCH_FORM_DATA_SUCCESS,payload: formData }) }) .catch(error => { // on error,do whatever is best for your use case dispatch({ type: actions.FETCH_FORM_DATA_ERROR,payload: error }) }) } } // reducer const INITIAL_STATE = { formData: {},error: {},fetching: false } export default function(state = INITIAL_STATE,action) { switch(action.type) { case FETCH_FORM_DATA_REQUEST: // when dispatch the 'request' action,toggle fetching to true return Object.assign({},state,{ fetching: true }) case FETCH_FORM_DATA_SUCCESS: return Object.assign({},{ fetching: false,formData: action.payload }) case FETCH_FORM_DATA_ERROR: return Object.assign({},error: action.payload }) } } // route can look something like this to access the formId in the URL if you want // I use this URL param in the component below but you can access this ID anyway you want: <Route path="/myForm/:formId" component={SomeForm} /> // form component class SomeForm extends Component { componentWillMount() { // get formId from route params const formId = this.props.params.formId this.props.fetchFormData(formId) } // in render just check if the fetching process is happening to know when to display the spinner // this could also be abstracted out into another method and run like so: {this.showFormOrSpinner.call(this)} render() { return ( <div className="some-form"> {this.props.fetching ? <img src="./assets/spinner.gif" alt="loading spinner" /> : <FormComponent formData={this.props.formData} /> } </div> ) } } function mapStateToProps(state) { return { fetching: state.form.fetching,formData: state.form.formData,error: state.form.error } } export default connect(mapStateToProps,{ fetchFormData })(SomeForm)