react-redux,applyMiddleware

react-redux详细教程请参考阮一峰的文章:react-redux,这里只是介绍一些基本用法,及自己遇到的一点痛点,applyMiddleware先介绍 两个常用的redux-thunkredux-logger

统一整理(想到哪写到哪…)

项目源码在github上戳react-plus

一,react-redux有什么用?

​ React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

  • UI组件只负责 UI 的呈现,不带有任何业务逻辑

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      render() {
      /**
      *state,add,del是哪里的?就是函数mapStateToProps和mapDispatchToProps返回的
      **/
      const {state,add,del}=this.props;
      return (
      <View style={styles.container}>
      <View style={styles.item}>
      <Text style={{color: 'red'}}>{state.sum}</Text>
      </View>
      <View style={[styles.item, {top: 10}]}>
      <TouchableWithoutFeedback onPress={add} >
      <View style={styles.touch}>
      <Text style={styles.but}>+</Text>
      </View>
      </TouchableWithoutFeedback>
      <View>
      <View>)
      }
  • 所有数据都由参数(this.props)提供

    • React-Redux 提供Provider组件,可以让容器组件拿到state

    • 1
      2
      3
      4
      5
      6
      7
      render(
      <Provider store={store}>
      <App />
      //然后App组件就可以通过this.props获取`每个组件JS文件内部定义的mapStateToProps中返回的state和mapDispatchToProps中返回的回调方法
      </Provider>,
      document.getElementById('root')
      )
  • React-Redux 提供connect方法,用于从 UI 组件生成容器组件

    • 1
      2
      3
      4
      5
      6
      function mapStateToProps(state){
      return {
      state: state.Index1
      //state是全局的state,Index1是Index1(reducer)返回的state,因为state内部包含所有reducer返回的state,所以这里需要用.语法确定需要传递那个reducer返回的状态给UI组件使用
      }
      }
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      function mapDispatchToProps(dispatch) {
      return{
      add: () => dispatch(IndexAction.add())①
      //IndexAction.add()执行Action中的一个函数,返回一个action对象
      }
      }

      /*action add()函数下面这样
      export function add() {
      return {
      type: 'INDEX_ADD'
      }
      }
      */
    • 1
      2
      export default connect(  mapStateToProps,  mapDispatchToProps)(UIComponent)
      //导出容器组件
  • 容器组件负责管理数据和业务逻辑

  • Reducer触发了mapDispatchToProps中的方法后自动执行

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      import * as types from '../constant/ActionTypes';

      const initialState = {
      sum:0
      };
      export default function IndexReducer(state = initialState, action = {}) {
      switch (action.type){
      case types.INDEX_ADD: ②
      return {
      ...state,
      sum:state.sum+1
      };
      break;
      case types.INDEX_DEL:
      return {
      ...state,
      sum:state.sum-1
      };
      break;
      default:
      return state;
      }
      }

      //执行了上面①出的dispatch方法后②出的reducer就会执行并返回最新的状态,那么问题来了,如果我想再来一个➕1的UI,怎么实现呢?直接增加一个UI容器组件,然后加一个reducer像下面这样?

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      import * as types from '../constant/ActionTypes';
      const initialState = {
      sum:0
      };
      export default function IndexReducer2(state = initialState, action = {}) {
      switch (action.type){
      case types.INDEX_ADD:
      return {
      ...state,
      sum:state.sum+1
      };
      break;
      case types.INDEX_DEL:
      return {
      ...state,
      sum:state.sum-1
      };
      break;
      case types.INDEX_INIT:
      return {
      ...state,
      sum:action.param!=undefined
      };
      break;
      default:
      return state;
      }
      }

      结果就是两个加操作会一起执行,点击第一个加按钮,第二个一起增加,原因是因为点击第一个加按钮和点击第二个加按钮执行的mapDispatchToProps方法都是触发相同的action:{ type: 'INDEX_ADD'}所以会触发所有监听此action的所有的reducer,解决的办法就是每个reducer监听自己的action,只要在action增加一个function add2(){ return { type: types.INDEX2_ADD} },然后设置IndexReducer2,

      case types.INDEX2_ADD:
      ​ return {
      ​ …state,
      ​ sum:state.sum+1
      ​ };
      ​ break;

      即可完各自reducer监听各自的action执行并返回各自的state

二,redux-logger有什么用?

就是说为了方便调试,redux-logger中间键会加入到store中后会自动监听状态更新,并在控制台打印出向相关信息

1
2
3
4
5
6
7
8
9
10
action @ 00:29:13.950 INDEX_ADD
prev state Object
IndexReduer:Object
sum:0
action Object
type:'INDEX_ADD'
next state Object
IndexReduer:Object
sum:1
这样的相关信息...

三,加入中间键后store的创建方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// rootReducer.js
import { combineReducers } from 'redux';
import IndexReducer from './IndexReducer';
import IndexReducer2 from './indexReducer2';
const RootReducer = combineReducers({
IndexReducer,IndexReducer2 //通过combineReducers合成一个总rootReducer
});
export default RootReducer;



// configure-store.js //用于生成store
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducer/RootReducer';
import thunk from 'redux-thunk';
const middlewares = [thunk];

const createLogger = require('redux-logger');
if (process.env.NODE_ENV === 'development') {
const logger = createLogger();
//redux 日志打印
middlewares.push(logger);
}
// store生成方式一,网上好像这样方式还比较多
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
return store;
}
// store生成方式二,还是感觉这种比较简单(可以直接在主界面使用)
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(...middlewares));
return store;
}

三,redux-thunk有什么用?

redux-thunk,是为了异步使用dispatch方法而使用的,和redux-logger一样,由store加入后自动生效,使用场景?我来小举一例:

例如上面的点击加一操作是这样的

1
2
3
4
5
6
7
1,  const {state,add,del}=this.props;
2, <Text style={styles.but} onPress={add}>+</Text>
3, function mapDispatchToProps(dispatch) {
return{
add: () => dispatch({type:'INDEX_ADD'})
}
}

那么我现在我需要实现点击第一个加一按钮3秒才执行加一,

不用redux-thunk我们可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
1, const {state,add,del}=this.props;
2, startAdd(){
setTimeout(function(){
add();
},3000)
}
3, <Text style={styles.but} onPress={this.startAdd}>+</Text>
4, function mapDispatchToProps(dispatch) {
return{
add: () => dispatch({type:'INDEX_ADD'})
}
}

使用了redux-thunk后可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
1, const {state,add,del}=this.props;
2, <Text style={styles.but} onPress={add}>+</Text>
3, function mapDispatchToProps(dispatch) {
return{
add: () => dispatch(function(dispatch,getState){
console.log(getState())
setTimeout(function(){
dispatch({type:'INDEX_ADD'})
},3000)
})
}
}

也就是说本来只能传入{}作为参数的dispatch用了redux-thunk后可以传一个函数作为参数,然后再这个函数内部执行dispatch就可以了,所有可以在函数内部执行setTimeout和fetch等方法了,实现异步的redux操作,同时thunk还支持第二个参数,用来获取全局state!