阅读 282

React Redux

基本工作流程

  1. 用户通过action creator创建action并派发dispatch action。
  2. store收到之后自动调用相应的reducer,传入当前的state和收到的action,返回新的state。
  3. state一旦发生变化,会调用监听函数,通知订阅了store的组件(store.subscribe(listener))。
  4. Reacr component中可以通过store.getState()获取到store的状态。
flow

中间件

上述流程中,我们的reducer只能处理一些同步的、无副作用的action,那一步操作怎么办?像数据获取这些有副作用的操作怎么办?这时我们可以使用redux中的新工具——中间件(middleware)。

所谓中间件,就是对原来的store.dispatch进行封装,在发出action和执行reducer之间添加一些操作。

中间件的用法

我们既可以使用现有的中间件(redux-thunk、redux-logger等),也可以自定义中间件:

const middleWare = (store) => (next) => (action) => {
	//派发action之前进行一些操作
  ...
  //dispatch这个action
	next(action)
  //action执行后
	...
}
复制代码

要使用中间件,只需在createStore的时候将applyMiddlewares(thunk, logger)参数传入即可。

异步操作的基本思路(使用redux-thunk)

假设说我们要向服务器请求数据,而且这个数据在多个模块中要用到,那么我们可以考虑在store中获取数据,并使用redux-thunk中间件,派发三种action,分别是

  • fetchDataStart
  • fetchDataSuccess
  • fetchDataFail

维护state对象,包含loading、data、error三个属性,分别表示是否加载数据中,获取到的data,出错信息。

我们的目标是将获取数据的操作放到store中,也就是在component中dispatch一个类似giveMeData这样的action,然后store收到这个action之后自动执行API请求、请求成功或失败后的处理。

//在react component中
componentDidMount(){
  dispatch(giveMeDataActionCreator())
}
复制代码

一般的action creator返回的都是一个对象,这没有办法满足我们的需求,这时我们就要引入redux-thunk这个中间件,它封装了dispatch这个方法,让dispatch多支持一种参数类型——函数类型。

giveMeDataCreator返回一个函数,带有dispatch和getState两个redux方法。

export const giveMeDataCreator = () : ThunkAction => (dispatch, getState) => {
  dispatch(fetchDataStartCreator());
  try{
    const { data } = await axios.get(...);
    dispatch(fetchDataSuccessCreator(data));
  } catch (e) {
    dispatch(fetchDataFailCreator(e.message));
  }
}
复制代码

在这个action creator中,我们连续发送两个action,让reducer完成相关操作。而我们的中间件会持续执行,直到异步逻辑全部结束。

普通的dispatch只支持对象类型的参数,redux-thunk这个中间件,添加了函数类型参数的支持,类似的,redux-promise添加了对promise类型参数的支持。

UI组件和容器组件

React-Redux将所有组件分为UI组件和容器组件两类。

UI组件特征:

  • 只负责UI的呈现,不带有任何业务逻辑。
  • 没有状态(即不使用this.state这个变量)
  • 所有参数由this.props提供
  • 不使用任何Redux的API

容器组件特征:

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 带有内部状态
  • 使用Redux的API

当一个组件既涉及UI呈现,又包含业务逻辑处理,我们可以把它拆分成这样的结构:外面一个容器组件,里面包含一个UI组件,前者负责从外部获取数据传给内部,后者负责根据传来的数据渲染出视图。

connect()

react-redux方法提供connect方法用于从UI组件中生成容器组件。使用方法如下:

export const Home = 
      connect(
        mapStateToProps,
        mapDispatchToProps)
				(UIComponent)
复制代码

这个方法后面跟两个括号,第一个括号接收两个参数mapStateToProps和mapDispatchToProps。第二个括号传入需要包裹的UI组件。

mapStateToProps

负责输入逻辑,将外部的state映射为内部的props。

const mapStateToProps = (state: RootState, ownProps) => {
  return {
    loading: state.recommendProducts.loading,
    error: state.recommendProducts.error,
    productList: state.recommendProducts.productList
  }
};
复制代码

是一个函数,接收外部的state为参数,返回一个对象,里面的每个键值对就是一个映射,比如说,通过this.props.loading,可以获取到store中state的loading属性。mapStateToProps会订阅store,当state更新时,会自动执行,重新计算UI组件的参数,触发重新渲染。

如果connect方法省略这个参数,UI组件就不会订阅store,也就是state的更新不会触发UI组件更新。

mapDispatchToProps

用来建立UI组件参数到store.dispatch方法的映射。它可以是一个函数,也可以是一个对象。

如果是函数,他会接收dispatch和ownProps作为参数,返回一个对象。

const mapDispatchToProps = (dispatch) => {
  return {
    giveMeData: () => {
      dispatch(giveMeDataActionCreator())
    }
  }
}
复制代码

如果mapDispatchToProps是对象,它的每一个键值是一个函数,被当作action creator。

const mapDispatchToProps = {
  giveMeData: () => {
    type:...,
    payload: ...
  }
}
复制代码

Provider

connect方法能生成容器组件,但要让容器获取到store中的state,才能生成UI组件的参数。

一种方便的方法是,使用react-redux提供的Provider组件。

import { Provider } from 'react-redux'
<Provider store={store}>
	<App />
</Provider>
复制代码

这样的包裹,使得App内所有子组件都可以通过context获取到state。

参考资料:

阮一峰的网络日志——Redux入门教程一/二/三

文章分类
前端
文章标签