React 中 Redux 的进阶玩法

3,296 阅读5分钟

如果你是Redux入门者,建议就此打住,直接跳到文末,那里有对初学者的建议…

Github地址:github.com/reactjs/red…
英文原版文档地址:redux.js.org
在线中文文档地址:cn.redux.js.org

前言

Redux是一个前端库,主要是用来解决为什么要写这篇文章呢?是因为自己之前在用Redux的时候,发现花样最繁多的当属在View界面上进行的操作了。而对于单纯的前端来讲,Redux的接入和在React中接入有所不同。接下来我们介绍一下在React中使用Redux时,View中的部分逻辑。

牵线React与Redux

在单纯的前端中使用Redux时,直接使用store里的两个函数就可以实现简单的store.getState()store.subscribe()以及store.dispatch(...) 几个核心函数即可完成StoreView的绑定和简单使用。而对于React来讲,需要使用一个中间件来实现Redux与React的无缝接入——react-redux, 一款同样由facebook的reactjs小组开源的一个配套库。( 突然发现这篇文章应该叫做‘react-redux的使用’才对,逃~ )

使用纯Redux来实现React中的应用

和上边讲的一致,这里我讲一下具体步骤:

  • 写好你的Reducer (这部分本文不做讲解)
    如下是项目中reducers里index.js部分的代码
    import {combineReducers} from 'redux'
    import viewTree from './viewTreeReducer'
    export default combineReducers({
        viewTree
    })

上述代码的作用,就是通过combineReducer函数将多个reducers进行合并然后导出之。至于文件名叫index.js可以在import的时候直接将路径写到文件夹级就可以了,系统会根据index.js来进行索引导入。

  • View界面的绑定和事件发送

    import reducers from 'your path of reducers'
    import { createStore } from 'redux'
    let store = createStore(reducers) // 为了避免重复,可以选择在其他地方创建好然后import进来
    ...
    let unsubscribe = store.subscribe(() => {
        let newState = store.getState() // 获取更新后最新的state树
        component.setState(...) // 这里的component可以是this
    })
    ...

这就完成了对store订阅的一个功能。一般而言,在setState的时候,没必要把整棵state树放进去,只需要根据我们创建state树的结构取出需要被监听修改的状态即可。
至于事件的发送,在你触发操作的方法里使用store.dispatch(...)就够了。需要填入参数就是你的Action,这个Action没什么特别的,其本质就是一个包含有type和其他参数的对象。Redux是建议我们给每个Action写ActionCreator来实现,将type固化到每一个actionCreator方法中。

  • 写一个ActionCreator例子 (这部分一般都被独立出来定义,建议写到对应的Reducer中,这样逻辑关系泾渭分明)
    plusOne(a, b, c) {
        return {
          type: 'PLUS_ONE',
          payload: {
            a, b, c  
          }
        } 
    }
    

type一般以常量的形式给出。这样,在导入了对应的ActionCreator方法之后,就可以通过store.dispatch(plusOne('poberWong', 'male', '1993'))的形式来派发这个Action了。

在项目中接入使用React-Redux

上述的用法让人觉得稍微有些死板,这里react-redux带你装逼带你飞

以下是React项目中index.js中的核心代码:

import React from 'react'
import { render } from 'react-dom'
import App from './containers/App'
import todoApp from './reducers'
render( 
  
  , document.getElementById('root'))

  • 接入的方式如下:
    1. 从redux中导入createStore方法,这个用来将写好的reducer包装为store
    2. 从react-redux 中导入Provider组件,用来包裹我们应用的根组件。其原理是利用组件的Context属性来实现对store的全局分发。
      经过处理的index.js 如下:
        import React from 'react'
        import { render } from 'react-dom'
        import { createStore } from 'redux'
        import { Provider } from 'react-redux'
        import App from './containers/App'
        import todoApp from './reducers'
        let store = createStore(todoApp)
        render( 
             
               
            
      , document.getElementById('root'))

在View中花式玩转Redux

  • 先呈上一个简单的Demo案例
    import React from 'react'
    import {increase, decrease} from 'it is the path'
    import {connect} from 'react-redux'
    class App extends React.Component{
      render() {
        return (
          
    {this.props.counter}
    ) } _onClick () { this.props.dispatch(increase(...)) } function select (state) { // 手动注入state,dispatch分发器被connect自动注入 return { // 注入的内容自行选择 counter: state.counter } } export default connect(select)(App)

这里的核心在于react-redux中connect的使用。

  • 最后的一个select方法是connect方法中的回调函数,负责向当前组件的props中注入state。而在该方法的return方法中,返回的是需要注入的东西。注: 在这里注入之后,就可以在状态树被修改后将值同步过来并重新调用render(), 因此将对应的值应用到与状态相关的模块中

  • 于是,如上select部分代码可以写成 export default connect(state => ({counter: state.counter}))(App)。 如果你需要将整棵状态树同步进来,可以简化为export default connect(state => state)(App)

  • 使用注解进一步简化代码:

    @connect(state => state)
    export default class extends React.Component{...} // 作为默认导出的模块,类名是可选的
    
  • 分发事件: this.props.dispatch(increase(2))

  • 实现dispatch与ActionCreator的绑定,进一步优化Redux的使用
    以上讲的就是如何通过react-redux这个库来实现Redux与React的对接。
    目前,光这样玩还不够过瘾。当我们实现actionCreator的绑定之后,就可以直接使用creator来派发action了。

    • 使用redux提供的bindActionCreator来绑定

      import {increase, descrease} from 'it is the path'
      const mapDispatchToProps = (dispatch) =>
      bindActionCreators({
            increase,
            decrease
      }, dispatch)
      @connect(state => state, mapDispatchToProps)

connect参数中的第一个回调方法是用来给当前组件的props注入state状态树的。第二个回调方法,即就是我们在这里用到的对dispatch的绑定。因此,如上代码同样可以用Lambda表达式改写为

@connect(state => state, dispatch => bindActionCreators({increase, decrease}, dispatch))

  • 此时,被绑定好dispatch的increase和decrease这两个ActionCreator就已经被注入到props中去了。因此我们在我们需要分发action的地方就可以这么写:this.props.increase(2)。是不是简单又好用呢?
    如果你有解构props或者state的习惯,又会出现如下代码:
    const {increase, decrease} = this.props
    ...
    increase(2)
    ...

结语

再次强调一下,本文不适合初学者看,本文旨在于React中提高Redux的使用效率以及理解之间的结构关系。如果想要入门Redux,笔者推荐中文文档的如下章节:

2.1 Action
2.2 Reducer
2.3 Store
2.4 数据流 (选读)
2.5 搭配 React