redux+react-redux+redux-thunk构建react的状态管理器

 

本文是学习redux时的一些感悟以及对流程的记录, 包含redux的同步及异步处理。

这里就不对redux和react-redux作过多的介绍了。直接结合代码来解释一些遇到的问题和理解。

1.目录结构

该目录是基于create-react-app生成的项目, 添加了redux文件夹存放redux的处理逻辑

├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── redux
│   │    ├── actions              // 将每个action按自己的方式拆分成多个, 便于管理
│   │    │   ├── action1.js       
│   │    │   └── action2.js       
│   │    ├── reducers
│   │    │   ├── modules          // 拆分recuder
│   │    │   │   ├── reducer1.js
│   │    │   │   └── reducer2.js
│   │    │   └── index.js         // 组合reducers并导出reducer
│   │    └── stores
│   │        ├── initialState.js  // 储存初始state
│   │        └── index.js         // 导出store
│   ├── views
│   │   └── Home.js
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   └── logo.svg
└── yarn.lock

2.用到的插件

主要用到了: redux, redux-thunk, react-redux

安装:

npm i redux redux-thhunk react-redux -S

3. 实现

3.1. store

import {createStore, compose, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'  // redux-thunk 用于处理异步操作

import {reducer} from '../reducer'  // 合并完的reducer
import initialState from './initialState'  // 初始的state

export const store = createStore(
  reducer,
  initialState,
  compose(applyMiddleware(thunk))  // 应用中间件, 如果只有一个中间件, 可以省略compose()直接写:appliMiddleware(middleware)
)

store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。

redux 提供createStore这个函数,用来生成 state。

createStore函数有三个参数, 分别是: reducer, preloadedState(也就是初始值), enhancer(也就是中间件), 其中只有reducer是必传的, 其他两个可以不传。同时, 如果第二个参数是函数类型, 会默认传的是enhancer。

3.2. action 

// action1.js
// 将type保存到一个对象中, 便于管理
const constant= {
  ADD:'ADD',
  REDUCE:"REDUCE"
}
export const ADD = {
  type:constant.ADD,
}
export const REDUCE = {
  type:constant.REDUCE,
}
//action2.js
const constant= {
  ADD_MSG: 'ADD_MSG',
  DEL_MSG: 'DEL_MSG'
}
// 异步触发action
export const ADD_MSG = msg => async (dispatch, getState) => dispatch({
  type: constant.ADD_MSG,
  payload: msg
})
export const DEL_MSG = msg => ({
  type: constant.DEL_MSG,
  payload: msg
})

 action 是一个对象。其中的type属性是必须的,表示 action 的名称。其他属性可以自由设置。

对比上面两个的action, 我们可以发现: 上面的action等于一个对象, 而下面的action是一个返回一个对象的函数。这是因为第一个是不需要做额外操作的, 所以直接把对象传给 reducer ,就可以交给reducer去进行操作了, 而如果需要传递参数, 就需要将参数通过函数返回的对象中带上该参数, 再去交给reducer处理, 具体会在组件中使用时讲到。

3.3. reducer

// 拆分的reducer
// reducer1.js, 命名导出
export const reducer1 = function (state=123, action) {
  switch(action.type){
    case 'ADD':
      return ++state
    case 'REDUCE': 
      return state-1
    default : 
      return state
  }
}
// reducer2.js  默认导出
export default function (state=[], action) {
  switch(action.type){
    case 'ADD_MSG':
      return [...state,action.payload]
    case 'DEL_MSG':
      return state.filter(item=>item.msg!==action.payload.msg)
    default : 
      return state
  }
}

 reducer 是一个函数,它接受 action 和当前 state 作为参数,返回一个新的 state。

注意: 每一个reducer接受的的state参数必须设置一个默认值, 否则会报错

// 组合拆分的reducers, 导出组合后的reducer
// reducers/index.js
import {combineReducers} from "redux";
import {reducer1} from './modules/reducer1'
import reducer2 from './modules/reducer2'

export const reducer = combineReducers({
  reducer1,
  msg: reducer2
})

将每个拆分的reducer通过combineReducers函数组合起来。

注意: combineReducer接受一个对象, 对象的键名为创建后的state的键名, 值为每个reducer, 所以最终生成的state应该是这样的:

{
    reducer: xxx,
    msg:{}
}

换句话就是说: 拆分reducer后, redux最终生成的state的键名与createStore函数中传入的初始值无关, 而是取决于combineReducers时传入对象的键名。

4. redux结合react

// App.js
import React, {Component} from 'react';
import {Provider} from 'react-redux'
import {store} from '@/redux/store'
import Home from './views/Home'

class App extends Component {
  render() {
    return (
       // 通过react-redux的Provider组件让使用connect方法生成的组件能拿到state
         
      
    )
  }
}
export default App;
// Home.js
import React, {Component} from 'react';
import {connect} from 'react-redux'
import {ADD,REDUCE} from '@/redux/actions/action1'
import {ADD_MSG,DEL_MSG} from '@/redux/actions/action2'

class Home extends Component {
  state={
    msg:'',
  }
  onInput=(e)=>{
    this.setState({
      msg:e.target.value
    })
  }
  addMsg = ()=>{
    const msg = {
      msg:this.state.msg
    }
  this.props.addMsg(msg)
    this.setState({
      msg:''
    })
  }
  delMsg = (msg)=>{
    this.props.delMsg(msg)
  }
  render() {
    return (
      

{this.props.num}

{ this.props.msg.map(item=>{ return (

{item.msg}

) }) } this.onInput(e)}/>
); } } const mapStateToProps = (state,ownProps)=>{ return { num: state.reducer1, msg:state.msg } } const mapDispatchToProps = (dispatch,ownProps)=>{ return{ addNum: ()=>dispatch(ADD), reduceNum: ()=>dispatch(REDUCE), addMsg: (msg)=>dispatch(ADD_MSG(msg)), delMsg: (msg)=>dispatch(DEL_MSG(msg)) } } export default connect(mapStateToProps,mapDispatchToProps)(Home)

 4.1. connect

react-redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

如上, connect接收mapStateToProps和mapDispatchToProps两个参数, 生成一个高阶函数, 然后传入UI组件Home, 生成一个新的组件, 该组件可以在props中访问到mapStateToProps中指定的state, mapDispatchToProps中指定UI 组件的参数到store.dispatch方法的映射了。

4.2. mapStateToProps

mapStateToProps是一个函数,它接受state作为参数,返回一个对象。这个对象有num和msg两个属性,代表 UI 组件的同名参数。

mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。

connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。

4.3. mapActionToProps

mapDispatchToPropsconnect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

如果mapDispatchToProps是一个函数,会得到dispatchownProps(容器组件的props对象)两个参数。

4.4. redux-thunk

总结一句话, thunk就是可以使store.dispatch可以接受函数作为参数。而在函数里我们就可以进行异步操作, 从而实现redux的异步操作。

具体的说明可以参考阮一峰的Redux 入门教程(二):中间件与异步操作

export const ADD_MSG = msg => async (dispatch, getState) => dispatch({
  type: constant.ADD_MSG,
  payload: msg
})

如上, 我们在本来应该返回对象的地方返回了一个async函数, 函数接收dispatch跟getState两个参数, 然后返回一个dispatch, 发出一个action, 这样就模拟了一个异步操作。

 

参考资料: 

Redux 入门教程(一):基本用法

Redux 入门教程(二):中间件与异步操作

Redux 入门教程(三):React-Redux 的用法

你可能感兴趣的:(react,#,redux)