搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > 前端面试官 > 我们什么需要异步的Redux中间件?

我们什么需要异步的Redux中间件?

前端面试官 2017-11-29

我们什么需要异步的Redux中间件?【译】


这个问题来自stackoverflow,我之所以找到它也是在看React的项目中基本都使用了Redux,但是有的使用了redux-thunk,有的没有,有的框架也使用了redux-thunk,到底怎么做才更合理呢?


问题详情

根据Redux文档,如果不使用中间件,Redux只支持同步的数据流动?我不明白为什么是这样的,为什么我们不把异步放在组件中然后使用同步的“dispatch”呢?


假如我们有这样的界面,一个输入框和一个按钮,点击按钮就会从服务端拉取数据



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>;    }}



这个方法可以正确的获取数据,那么我为什么还要用redux-thunk或者redux-promise呢?



最佳回答

这种方法没有错。 在一个大的应用程序中,这只是不方便的,因为你将会有不同的组件执行相同的动作,比如空闲一段时间再去做什么事情(比如自动填充的场景),又或者是保存一些本地状态如自增ID之类。所以它只是从维护的角度来方便大家提炼一些制造action的函数。


你可以阅读我在问题“如何延迟派发一个redux action”中更为详尽的回答


像Redux Thunk或Redux Promise这样的中间件只是给你“语法糖”来调度thunk或promises,但是你不必使用它。


所以如果你不使用中间件,你的代码应该类似这样


// action creatorfunction 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 })    );}// componentcomponentWillMount() {  loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch}


但是如果使用了thunk中间件,你就可以这么写了


// action creatorfunction 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 })    );}// componentcomponentWillMount() {  this.props.dispatch(loadData(this.props.userId)); // dispatch like you usually do}


所以没有太大的区别,我喜欢后一种方法的原因是组件不关心动作创建者是异步的。 它只是调用dispatch正常,它也可以使用mapDispatchToProps绑定这样的动作创造者与一个简短的语法等。组件不知道如何实现动作创建者,你可以切换不同的异步方法(Redux Thunk,Redux Promise, Redux Saga)而不更改组件。 另一方面,使用前者的显式方法,组件确切地知道特定的调用是异步的,并且需要通过一些约定来传递调度(例如,作为同步参数)。 也想想这个代码将如何改变。 假设我们想要有第二个数据加载功能,并将它们合并到一个单独的动作创建器中。


在第一种方法中,我们需要注意我们正在调用什么样的动作创造者:


// action creatorsfunction 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  );}// componentcomponentWillMount() {  loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first}


使用Redux Thunk动作创建者可以派发其他动作创建者的结果,甚至不用考虑它们是同步的还是异步的:


// action creatorsfunction 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!  );}// componentcomponentWillMount() {  this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally!}


使用这种方法,如果您稍后希望动作创建者查看当前的Redux状态,则可以使用传递给Thunk的第二个getState参数,而无需修改调用代码:

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 })      );  }}


如果您需要将其更改为同步,则也可以在不更改任何调用代码的情况下执行此操作:

// 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')  }}


在所以使用Redux Thunk或Redux Promise等中间件的好处是,组件不知道动作创建者是如何实现的,他们是否在意Redux状态,它们是同步的还是异步的,是否调用其他动作创建者。 缺点是有些间接,但是我们相信它在实际应用中是值得的。



Redux是React的好基友,而且哥俩都有洁癖,只做分内的事,把分内的事做好,周边的支持需要一些中间件、社区工具来让它们变得更好用。总结下来redux-thunk有两点好处,一个是可以抽离一些逻辑让代码更容易维护,另一个是可以屏蔽同步异步动作的使用区别以后只需要修改action的实现就可以了,所以还是很有必要了解一下的




版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《我们什么需要异步的Redux中间件?》的版权归原作者「前端面试官」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注前端面试官微信公众号

前端面试官微信公众号:junjunpublic

前端面试官

手机扫描上方二维码即可关注前端面试官微信公众号

前端面试官最新文章

精品公众号随机推荐