sbi*_*nko 600 javascript asynchronous reactjs redux redux-thunk
根据文档,"没有中间件,Redux商店只支持同步数据流".我不明白为什么会这样.为什么容器组件不能调用异步API,然后调用dispatch
操作?
例如,想象一个简单的UI:字段和按钮.当用户按下按钮时,该字段将填充来自远程服务器的数据.
import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';
const ActionTypes = {
STARTED_UPDATING: 'STARTED_UPDATING',
UPDATED: 'UPDATED'
};
class AsyncApi {
static getFieldValue() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 100));
}, 1000);
});
return promise;
}
}
class App extends React.Component {
render() {
return (
<div>
<input value={this.props.field}/>
<button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
{this.props.isWaiting && <div>Waiting...</div>}
</div>
);
}
}
App.propTypes = {
dispatch: React.PropTypes.func,
field: React.PropTypes.any,
isWaiting: React.PropTypes.bool
};
const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
switch (action.type) {
case ActionTypes.STARTED_UPDATING:
return { ...state, isWaiting: true };
case ActionTypes.UPDATED:
return { ...state, isWaiting: false, field: action.payload };
default:
return state;
}
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
(state) => {
return { ...state };
},
(dispatch) => {
return {
update: () => {
dispatch({
type: ActionTypes.STARTED_UPDATING
});
AsyncApi.getFieldValue()
.then(result => dispatch({
type: ActionTypes.UPDATED,
payload: result
}));
}
};
})(App);
export default class extends React.Component {
render() {
return <Provider store={store}><ConnectedApp/></Provider>;
}
}
Run Code Online (Sandbox Code Playgroud)
渲染导出的组件时,我可以单击该按钮并正确更新输入.
注意通话中的update
功能connect
.它调度一个动作,告诉应用程序它正在更新,然后执行异步调用.调用完成后,将提供的值作为另一个操作的有效负载进行调度.
这种方法有什么问题?为什么我要使用Redux Thunk或Redux Promise,正如文档所示?
编辑:我搜索了Redux仓库的线索,发现Action Creators过去必须是纯函数.例如,这是一个用户试图为异步数据流提供更好的解释:
动作创建者本身仍然是一个纯函数,但它返回的thunk函数不需要,它可以执行我们的异步调用
动作创作者不再需要纯粹.因此,thunk/promise中间件在过去绝对是必需的,但似乎不再是这种情况了?
Dan*_*mov 625
这种方法有什么问题?为什么我要使用Redux Thunk或Redux Promise,正如文档所示?
这种方法没有错.这在大型应用程序中不方便,因为您将有不同的组件执行相同的操作,您可能想要去除某些操作,或保持一些本地状态,如自动递增ID靠近动作创建者等.所以它更容易从维护的角度来将动作创建者提取到单独的函数中.
您可以阅读我的答案"如何使用超时分派Redux操作",以获得更详细的演练.
像终极版咚或终极版无极中间件只是给你"语法糖"派遣的thunk或承诺,但你不必须使用它.
因此,没有任何中间件,您的动作创建者可能看起来像
// action creator
function loadData(dispatch, userId) { // needs to dispatch, so it is first argument
return fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
);
}
// component
componentWillMount() {
loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
}
Run Code Online (Sandbox Code Playgroud)
但是使用Thunk Middleware你可以像这样写:
// action creator
function loadData(userId) {
return dispatch => fetch(`http://data.com/${userId}`) // Redux Thunk handles these
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
);
}
// component
componentWillMount() {
this.props.dispatch(loadData(this.props.userId)); // dispatch like you usually do
}
Run Code Online (Sandbox Code Playgroud)
所以没有太大的区别.我喜欢后一种方法的一件事是组件不关心动作创建者是异步的.它只是dispatch
正常调用,它也可以用mapDispatchToProps
短语法绑定这样的动作创建者等.组件不知道如何实现动作创建者,你可以在不同的异步方法之间切换(Redux Thunk,Redux Promise,Redux Saga) )无需更改组件.另一方面,使用前一种显式方法,您的组件确切地知道特定调用是异步的,并且需要dispatch
通过某种约定传递(例如,作为同步参数).
还要考虑一下这段代码将如何变化.假设我们想要第二个数据加载功能,并将它们组合在一个动作创建器中.
使用第一种方法,我们需要注意我们正在调用的动作创建者:
// action creators
function loadSomeData(dispatch, userId) {
return fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
);
}
function loadOtherData(dispatch, userId) {
return fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
);
}
function loadAllData(dispatch, userId) {
return Promise.all(
loadSomeData(dispatch, userId), // pass dispatch first: it's async
loadOtherData(dispatch, userId) // pass dispatch first: it's async
);
}
// component
componentWillMount() {
loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first
}
Run Code Online (Sandbox Code Playgroud)
使用Redux Thunk动作创建者可以获得dispatch
其他动作创建者的结果,甚至不会认为这些是同步还是异步:
// action creators
function loadSomeData(userId) {
return dispatch => fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
);
}
function loadOtherData(userId) {
return dispatch => fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
);
}
function loadAllData(userId) {
return dispatch => Promise.all(
dispatch(loadSomeData(userId)), // just dispatch normally!
dispatch(loadOtherData(userId)) // just dispatch normally!
);
}
// component
componentWillMount() {
this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally!
}
Run Code Online (Sandbox Code Playgroud)
使用这种方法,如果您以后希望您的动作创建者查看当前的Redux状态,您可以使用getState
传递给thunk 的第二个参数而不需要修改调用代码:
function loadSomeData(userId) {
// Thanks to Redux Thunk I can use getState() here without changing callers
return (dispatch, getState) => {
if (getState().data[userId].isLoaded) {
return Promise.resolve();
}
fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您需要将其更改为同步,您也可以在不更改任何调用代码的情况下执行此操作:
// I can change it to be a regular action creator without touching callers
function loadSomeData(userId) {
return {
type: 'LOAD_SOME_DATA_SUCCESS',
data: localStorage.getItem('my-data')
}
}
Run Code Online (Sandbox Code Playgroud)
因此,使用Redux Thunk或Redux Promise等中间件的好处是组件不知道如何实现动作创建器,以及它们是否关心Redux状态,它们是同步还是异步,以及它们是否称为其他动作创建者.缺点是一点间接,但我们相信它在实际应用中是值得的.
最后,Redux Thunk和朋友只是Redux应用程序中异步请求的一种可能方法.另一个有趣的方法是Redux Saga,它允许您定义长时间运行的守护进程("sagas"),它们在进行操作时执行操作,并在输出操作之前转换或执行请求.这将逻辑从动作创作者转变为传奇.您可能想要查看它,然后选择最适合您的选项.
我搜索了Redux回购中的线索,发现Action Creators过去必须是纯函数.
这是不正确的.文档说这个,但文档错了.
行动创造者从未被要求成为纯粹的职能.
我们修改了文档来反映这一点.
Seb*_*ber 417
丹阿布拉莫夫的回答是对的,redux-thunk
但我会更多地讨论一下相似但更强大的redux-saga.
redux-thunk
势在必行/ redux-saga
是声明性的如果你手上有一个thunk,比如IO monad或promise,你就不能轻易知道执行后它会做什么.测试thunk的唯一方法是执行它,并模拟调度程序(如果它与更多东西交互,则模拟整个外部世界......).
如果您使用的是模拟,那么您就不会进行函数式编程.
从副作用的镜头看,模拟是你的代码不纯的标志,在功能程序员的眼中,证明出现了问题.我们不应该下载图书馆以帮助我们检查冰山是否完整,而应该绕着它航行.一个铁杆的TDD/Java人曾经问我如何在Clojure中进行模拟.答案是,我们通常不这样做.我们通常将其视为我们需要重构代码的标志.
sagas(因为它们已经实现redux-saga
)是声明性的,就像Free monad或React组件一样,它们更容易测试而不需要任何模拟.
另见本文:
在现代FP中,我们不应该编写程序 - 我们应该编写程序的描述,然后我们可以随意内省,转换和解释.
(实际上,Redux-saga就像一个混合体:流程势在必行,但效果是声明性的)
关于CQRS/EventSourcing和Flux/Redux等后端概念可能与之相关的前端世界存在很多混淆,主要是因为在Flux中我们使用术语"动作",它有时代表命令式代码(LOAD_USER
)和事件(USER_LOADED
).我相信像事件采购一样,你应该只发送事件.
想象一个带有用户个人资料链接的应用.使用这两个中间件处理这个问题的惯用方法是:
redux-thunk
<div onClick={e => dispatch(actions.loadUserProfile(123)}>Robert</div>
function loadUserProfile(userId) {
return dispatch => fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'USER_PROFILE_LOADED', data }),
err => dispatch({ type: 'USER_PROFILE_LOAD_FAILED', err })
);
}
Run Code Online (Sandbox Code Playgroud)
redux-saga
<div onClick={e => dispatch({ type: 'USER_NAME_CLICKED', payload: 123 })}>Robert</div>
function* loadUserProfileOnNameClick() {
yield* takeLatest("USER_NAME_CLICKED", fetchUser);
}
function* fetchUser(action) {
try {
const userProfile = yield fetch(`http://data.com/${action.payload.userId }`)
yield put({ type: 'USER_PROFILE_LOADED', userProfile })
}
catch(err) {
yield put({ type: 'USER_PROFILE_LOAD_FAILED', err })
}
}
Run Code Online (Sandbox Code Playgroud)
这个传奇转换为:
每次单击用户名时,都会获取用户配置文件,然后使用加载的配置文件调度事件.
如您所见,有一些优点redux-saga
.
使用takeLatest
许可表示您只对获取上次用户名的数据感兴趣(如果用户在很多用户名上非常快速地点击,则处理并发问题).这种东西很难用thunk.takeEvery
如果您不想要这种行为,可以使用.
你让动作创作者保持纯洁.请注意,保留actionCreators(在sagas put
和组件中dispatch
)仍然很有用,因为它可能有助于您将来添加操作验证(assertions/flow/typescript).
由于效果是声明性的,因此您的代码变得更加可测试
您不再需要触发类似rpc的调用了actions.loadUser()
.您的UI只需要发送已经发生的事情.我们只发射事件(总是以过去时态!)而不再是动作.这意味着您可以创建解耦的"ducks"或Bounded Contexts,并且saga可以充当这些模块化组件之间的耦合点.
这意味着您的视图更易于管理,因为他们不再需要在已发生的事件和作为效果的事件之间包含该转换层
例如,想象一个无限滚动视图.CONTAINER_SCROLLED
可以导致NEXT_PAGE_LOADED
,但是可滚动容器的责任是决定我们是否应该加载另一个页面?然后他必须知道更复杂的事情,比如最后一页是否成功加载,或者是否已经有一个试图加载的页面,或者是否还有剩余的项目要加载?我不这么认为:为了获得最大的可重用性,可滚动容器应该只描述它已滚动.页面的加载是该滚动的"业务效果"
有些人可能会争辩说,生成器可以通过局部变量固有地隐藏在redux存储区之外的状态,但是如果你开始通过启动计时器等在thunk内部编排复杂的东西,那么无论如何都会遇到同样的问题.select
现在有一种效果可以从你的Redux商店获得一些状态.
Sagas可以进行时间旅行,还可以实现当前正在进行的复杂流量记录和开发工具.以下是一些已经实现的简单异步流日志:
Sagas不仅取代了redux thunk.它们来自后端/分布式系统/事件源.
这是一个非常普遍的误解,传奇只是在这里以更好的可测试性取代你的redux thunk.实际上这只是redux-saga的一个实现细节.使用声明效果优于thunks的可测试性,但是saga模式可以在命令式或声明性代码之上实现.
首先,saga是一个允许协调长期运行事务(最终一致性)和跨不同有界上下文(域驱动设计术语)的事务的软件.
为了简化前端世界,假设有widget1和widget2.当单击widget1上的某个按钮时,它应该对widget2有影响.而不是将两个小部件耦合在一起(即widget1调度以widget2为目标的动作),widget1仅调度其按钮被单击.然后saga侦听此按钮单击,然后通过分析widget2知道的新事件来更新widget2.
这增加了一个简单应用程序不必要的间接级别,但是可以更容易地扩展复杂的应用程序.您现在可以将widget1和widget2发布到不同的npm存储库,这样他们就不必了解彼此,也不必共享全局的操作注册表.这两个小部件现在是有限的上下文,可以单独生活.他们不需要彼此保持一致,也可以在其他应用程序中重复使用.传奇是两个小部件之间的耦合点,它们以有意义的方式为您的业务协调它们.
关于如何构建Redux应用程序的一些很好的文章,您可以使用Redux-saga解耦原因:
我希望我的组件能够触发应用内通知的显示.但我不希望我的组件高度耦合到具有自己的业务规则的通知系统(同时显示最多3个通知,通知排队,4秒显示时间等...).
我不希望我的JSX组件决定何时显示/隐藏通知.我只是让它能够请求通知,并将复杂的规则留在传奇中.这种东西很难用thunks或promises来实现.
传奇这个词来自后端世界.我最初在长时间的讨论中介绍了Yassine(Redux-saga的作者).
最初,这个术语是用一篇论文引入的,saga模式应该用于处理分布式事务中的最终一致性,但是它的用法已经被后端开发人员扩展到更广泛的定义,所以它现在也涵盖了"流程管理器"模式(不知何故,原始的传奇模式是流程管理器的一种特殊形式).
今天,"saga"一词令人困惑,因为它可以描述两种不同的东西.正如它在redux-saga中使用的那样,它没有描述处理分布式事务的方法,而是一种协调应用程序中的操作的方法.redux-saga
也可以被称为redux-process-manager
.
也可以看看:
如果您不喜欢使用生成器的想法,但是您对saga模式及其解耦属性感兴趣,那么您也可以使用redux-observable实现相同的目的,它使用名称epic
来描述完全相同的模式,但使用RxJS.如果您已经熟悉Rx,那么您会感到宾至如归.
const loadUserProfileOnNameClickEpic = action$ =>
action$.ofType('USER_NAME_CLICKED')
.switchMap(action =>
Observable.ajax(`http://data.com/${action.payload.userId}`)
.map(userProfile => ({
type: 'USER_PROFILE_LOADED',
userProfile
}))
.catch(err => Observable.of({
type: 'USER_PROFILE_LOAD_FAILED',
err
}))
);
Run Code Online (Sandbox Code Playgroud)
yield put(someActionThunk)
如果有意义的话,不要犹豫是否发出雷鸣声.如果您害怕使用Redux-saga(或Redux-observable)但只需要解耦模式,请检查redux-dispatch-subscribe:它允许监听调度并在侦听器中触发新的调度.
const unsubscribe = store.addDispatchListener(action => {
if (action.type === 'ping') {
store.dispatch({ type: 'pong' });
}
});
Run Code Online (Sandbox Code Playgroud)
acj*_*jay 28
简短的回答:对我来说,异步问题似乎是一种完全合理的方法.有几个警告.
在我刚刚开始工作的新项目时,我有一个非常相似的思路.我是香草Redux优雅系统的忠实粉丝,用于更新商店和重新渲染组件,使其远离React组件树的内核.对我来说,用这种优雅的dispatch
机制来处理异步似乎很奇怪.
我最终采用了一种非常类似的方法来处理我在项目中所拥有的内容,这是我们的项目,我们称之为react-redux-controller.
由于以下几个原因,我最终没有采用上述方法:
dispatch
通过词法范围访问自身.一旦该connect
语句失控,这就限制了重构的选项- 并且仅使用这一update
方法看起来非常笨拙.因此,如果将它们分解为单独的模块,您需要一些系统来让您编写这些调度程序功能.总而言之,您必须安装一些允许的系统,dispatch
并将商店注入您的调度功能以及事件的参数.我知道这种依赖注入的三种合理方法:
dispatch
中间件方法,但我认为它们基本相同.connect
,而不是必须直接使用原始的规范化存储.this
通过各种可能的机制将它们注入上下文,从而实现面向对象的方式.更新
在我看来,这个难题的一部分是反应 - 减少的限制.connect
获取状态快照但不是调度的第一个参数.第二个参数得到调度而不是状态.两个参数都没有得到关闭当前状态的thunk,因为能够在继续/回调时看到更新状态.
XML*_*XML 19
阿布拉莫夫的目标 - 理想的每个人 - 只是将复杂性(和异步调用)封装在最合适的地方.
在标准Redux数据流中哪里是最好的地方?怎么样:
Mic*_*ley 13
要回答开头提出的问题:
为什么容器组件不能调用异步API,然后调度操作?
请记住,这些文档适用于Redux,而不是Redux和React.连接到React组件的 Redux存储可以完全按照您的意思执行,但是没有中间件的Plain Jane Redux存储不接受dispatch
除了普通ol对象之外的参数.
没有中间件,你当然可以做到
const store = createStore(reducer);
MyAPI.doThing().then(resp => store.dispatch(...));
Run Code Online (Sandbox Code Playgroud)
但这是一个类似的情况,其中异步是围绕 Redux而不是由 Redux 处理.因此,中间件通过修改可以直接传递给的内容来允许异步dispatch
.
也就是说,我认为你的建议的精神是有效的.在Redux + React应用程序中,当然还有其他方法可以处理异步.
使用中间件的一个好处是,您可以继续正常使用动作创建器,而不必担心它们是如何连接起来的.例如,使用redux-thunk
,您编写的代码看起来很像
function updateThing() {
return dispatch => {
dispatch({
type: ActionTypes.STARTED_UPDATING
});
AsyncApi.getFieldValue()
.then(result => dispatch({
type: ActionTypes.UPDATED,
payload: result
}));
}
}
const ConnectedApp = connect(
(state) => { ...state },
{ update: updateThing }
)(App);
Run Code Online (Sandbox Code Playgroud)
它看起来与原始版本没有什么不同 - 它只是改组了一点 - 并且connect
不知道那updateThing
是(或需要)异步的.
如果您还想支持promises,observables,sagas或疯狂的自定义和高度声明性的动作创建者,那么Redux只需更改您传递的内容dispatch
(也就是您从动作创建者返回的内容)即可.不需要使用React组件(或connect
调用).
好的,让我们开始看看中间件如何工作,这完全回答了问题,这是Redux中的源代码和pplyMiddleWare函数:
function applyMiddleware() {
for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
return function (createStore) {
return function (reducer, preloadedState, enhancer) {
var store = createStore(reducer, preloadedState, enhancer);
var _dispatch = store.dispatch;
var chain = [];
var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch(action) {
return _dispatch(action);
}
};
chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
_dispatch = compose.apply(undefined, chain)(store.dispatch);
return _extends({}, store, {
dispatch: _dispatch
});
};
};
}
Run Code Online (Sandbox Code Playgroud)
看看这个部分,看看我们的调度如何成为一个功能.
...
getState: store.getState,
dispatch: function dispatch(action) {
return _dispatch(action);
}
Run Code Online (Sandbox Code Playgroud)
- 请注意,每个中间件都将被赋予
dispatch
和getState
作为命名参数的功能.
好吧,这就是Redux-thunk作为Redux最常用的中间件之一的介绍:
Redux Thunk中间件允许您编写返回函数而不是动作的动作创建者.thunk可用于延迟动作的发送,或仅在满足某个条件时发送.内部函数接收存储方法dispatch和getState作为参数.
所以如你所见,它将返回一个函数而不是一个动作,这意味着你可以随时随地调用它,因为它是一个函数......
那是什么笨蛋?这就是它在维基百科中的介绍:
在计算机编程中,thunk是一个子程序,用于将另一个计算注入另一个子程序.Thunks主要用于延迟计算直到需要,或者在另一个子例程的开头或结尾插入操作.它们具有编译器代码生成和模块化编程的各种其他应用程序.
该术语起源于"思考"的jocular派生词.
thunk是一个包装表达式以延迟其评估的函数.
//calculation of 1 + 2 is immediate
//x === 3
let x = 1 + 2;
//calculation of 1 + 2 is delayed
//foo can be called later to perform the calculation
//foo is a thunk!
let foo = () => 1 + 2;
Run Code Online (Sandbox Code Playgroud)
所以看看概念是多么容易,以及它如何帮助您管理异步操作......
没有它你就可以生活,但是在编程中记住总是有更好,更整洁和正确的方法来做事......
小智 6
使用 Redux-saga 是 React-redux 实现中最好的中间件。
例如:store.js
import createSagaMiddleware from 'redux-saga';
import { createStore, applyMiddleware } from 'redux';
import allReducer from '../reducer/allReducer';
import rootSaga from '../saga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
allReducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga);
export default store;
Run Code Online (Sandbox Code Playgroud)
然后 saga.js
import {takeLatest,delay} from 'redux-saga';
import {call, put, take, select} from 'redux-saga/effects';
import { push } from 'react-router-redux';
import data from './data.json';
export function* updateLesson(){
try{
yield put({type:'INITIAL_DATA',payload:data}) // initial data from json
yield* takeLatest('UPDATE_DETAIL',updateDetail) // listen to your action.js
}
catch(e){
console.log("error",e)
}
}
export function* updateDetail(action) {
try{
//To write store update details
}
catch(e){
console.log("error",e)
}
}
export default function* rootSaga(){
yield [
updateLesson()
]
}
Run Code Online (Sandbox Code Playgroud)
然后 action.js
export default function updateFruit(props,fruit) {
return (
{
type:"UPDATE_DETAIL",
payload:fruit,
props:props
}
)
}
Run Code Online (Sandbox Code Playgroud)
然后reducer.js
import {combineReducers} from 'redux';
const fetchInitialData = (state=[],action) => {
switch(action.type){
case "INITIAL_DATA":
return ({type:action.type, payload:action.payload});
break;
}
return state;
}
const updateDetailsData = (state=[],action) => {
switch(action.type){
case "INITIAL_DATA":
return ({type:action.type, payload:action.payload});
break;
}
return state;
}
const allReducers =combineReducers({
data:fetchInitialData,
updateDetailsData
})
export default allReducers;
Run Code Online (Sandbox Code Playgroud)
然后 main.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/components/App.jsx';
import {Provider} from 'react-redux';
import store from './app/store';
import createRoutes from './app/routes';
const initialState = {};
const store = configureStore(initialState, browserHistory);
ReactDOM.render(
<Provider store={store}>
<App /> /*is your Component*/
</Provider>,
document.getElementById('app'));
Run Code Online (Sandbox Code Playgroud)
试试这个..正在工作
有同步动作创建者,然后有异步动作创建者。
同步动作创建器是一种当我们调用它时,它立即返回一个 Action 对象,其中包含附加到该对象的所有相关数据,并准备好由我们的减速器处理。
异步动作创建器需要一点时间才能准备好最终分派动作。
根据定义,任何时候你有一个发出网络请求的动作创建者,它总是有资格作为异步动作创建者。
如果您想在 Redux 应用程序中使用异步操作创建器,则必须安装称为中间件的东西,它允许您处理这些异步操作创建器。
您可以在告诉我们将自定义中间件用于异步操作的错误消息中验证这一点。
那么什么是中间件,为什么我们需要它来实现 Redux 中的异步流?
在 redux 中间件(例如 redux-thunk)的上下文中,中间件帮助我们处理异步动作创建者,因为这是 Redux 无法开箱即用的处理。
将中间件集成到 Redux 循环中后,我们仍在调用动作创建者,这将返回一个将被分派的动作,但是现在当我们分派一个动作时,而不是直接将其发送给我们的所有减速器,我们将说一个动作将通过应用程序内的所有不同中间件发送。
在单个 Redux 应用程序中,我们可以拥有任意数量的中间件。在大多数情况下,在我们从事的项目中,我们会将一两个中间件连接到我们的 Redux 商店。
中间件是一个普通的 JavaScript 函数,它将在我们分派的每一个动作中被调用。在该函数内部,中间件有机会阻止将动作分派到任何减速器,它可以修改动作或以任何方式处理动作,例如,我们可以创建一个控制台记录的中间件您发送的每一个动作只是为了您的观看乐趣。
您可以将大量开源中间件作为依赖项安装到项目中。
您不仅限于使用开源中间件或将它们安装为依赖项。您可以编写自己的自定义中间件并在您的 Redux 存储中使用它。
中间件最流行的用途之一(并得到你的答案)是处理异步动作创建者,可能是最流行的中间件是 redux-thunk,它是关于帮助你处理异步动作创建者。
还有许多其他类型的中间件也可以帮助您处理异步动作创建者。
回答问题:
为什么容器组件不能调用async API,然后dispatch action?
我会说至少有两个原因:
第一个原因是关注点分离,action creator
调用 theapi
并取回数据不是 the 的工作,您必须将两个参数传递给您的action creator function
、 theaction type
和 a payload
。
第二个原因是因为redux store
正在等待具有强制操作类型和可选 a payload
(但在这里您也必须传递有效负载)的普通对象。
动作创建者应该是一个简单的对象,如下所示:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
Run Code Online (Sandbox Code Playgroud)
和的工作Redux-Thunk midleware
,以dispache
您的结果api call
来适当action
。