我有mapStateToProps
工作流程,但如果我想以不适合状态的方式响应操作=>道具范式?例如:
this.props.dispatch(saveArticle(...))
// on successful save, redirect to article page
如果我只是使用常规的旧XHR而不是动作,它看起来像这样:
saveArticle(...).then(article => this.router.push(`/articles/${article.id}`))
目前尚不清楚这将如何适应标准的React / Redux工作流程;我见过有人建议saveArticle()
动作创建者可以启动路由器更改,但我想保持这些分开;我应该能够在不被强制重定向的情况下保存文章。
解决方法可能是在 mapStateToProps
中执行;让动作设置一个标志或类似的东西,比如articleWasSaved
,并让组件为该支柱执行保存查找并重定向(如果它看到它),但这看起来真的很难看,特别是如果多个事情正在寻找更新,因为它可能需要组件清除标志。
我缺少一个简单/标准的解决方案吗?
答案 0 :(得分:2)
Redux-thunk 允许您将函数分派为操作。理想情况下,调度异步操作。
我在这里创建了一个例子,我觉得它对你有用:
export const tryingAsyncAction = () => {
return {
type: 'TRYING_ASYNC_ACTION'
}
}
export const actionCompleted = () => {
return {
type: 'ACTION_COMPLETED'
}
}
export const errorAsyncAction = (error) => {
return {
type: 'ERROR_ASYNC_ACTION',
error
}
}
export function someAsynAction() {
return dispatch => {
dispatch(tryingAsyncAction())
ApiService.callToAsyncApi(...)
.then(() => {
dispatch(actionCompleted())
}, (cause) => {
dispatch(errorAsyncAction(cause))
})
}
}
const initialState = {
tryingAction: false,
actionCompleted: false,
error: null,
shouldRedirect: false,
redirectUrl: null
}
export default function reducer(state = initialState, action) {
switch (action.type) {
case 'TRYING_ASYNC_ACTION':
return Object.assign({}, state, {
tryingAction: true
})
case 'ACTION_COMPLETED':
return Object.assign({}, state, {
tryingAction: false,
actionCompleted: true,
shouldRedirect: true,
redirectUrl: 'someUrl'
})
case 'ERROR_ASYNC_ACTION':
return Object.assign({}, state, {
tryingAction: false,
actionCompleted: false,
error: action.error
})
default:
return state
}
}
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk' //npm install --save redux-thunk
//Other imports...
const store = createStore(
reducer,
applyMiddleware(
thunkMiddleware
)
)
componentWillReceiveProps(nextProps){
if(nextProps.shouldRedirect && nextProps.redirectUrl)
this.router.push(`/articles/${article.id}`)
}
如果有什么你不明白,请告诉我。我会尽力澄清
答案 1 :(得分:1)
在这种情况下,您可以使用react-thunk
。
动作/ index.js
export function saveArticle(data) {
return (dispatch, getState) => (
api.post(data).then(response => {
dispatch({ type: 'SAVE_ARTICLE', payload: response })
return response;
})
)
}
减速器/ index.js
import { combineReducers } from 'redux';
const initialState = {
list: [],
current: null,
shouldRedirect: false,
redirectTo: null
};
export function articles(state = initialState, action) {
switch(action.type) {
case 'SAVE_ARTICLE':
return {
shouldRedirect: true,
redirectTo: '/some/url',
current: action.payload,
list: [...state.list, action.payload]
};
default: return state;
}
}
export default combineReducers({ articles });
存储/ index.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
// Note: this API requires redux@>=3.1.0
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
组件/ index.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as Actions from 'actions/index';
class MyComponent extends Component {
_handleSubmit = () => {
// get form values somehow...
// const values = getFormValues();
this.props.saveArticle(values).then(response => {
// you can handle you redirect here as well,
// since saveArticle is returning a promise
});
};
componentWillReceiveProps(nextProps) {
// you can handle the redirection here listening to changes
// on shouldRedirect and redirectTo that will be triggered
// when the action 'SAVE_ARTICLE' is dispatched
if(nextProps.shouldRedirect && nextProps.redirectTo) {
this.routes.push(nextProps.redirectTo);
}
}
render() {
// just an example
return (
<form onSubmit={this._handleSubmit}>
{ /* ... other elements here */ }
</form>
)
}
}
export default connect(
state => ({
articles: state.articles.list,
article: state.articles.current,
redirectTo: state.articles.redirectTo,
shouldRedirect: state.articles.shouldRedirect
}),
Actions
)(MyComponent);
PS:我在这里使用了一些babel语法糖,因此请确保您在.babelrc
中设置了以下预设。
es2015
stage-2
stage-0
react