React中Redux的进阶玩儿法

96
Pober_Wong 595a1b60 08f6 4beb 998f 2bf55e230555
2016.04.11 15:45* 字数 1426

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

Github地址:https://github.com/reactjs/redux
英文原版文档地址:http://redux.js.org
在线中文文档地址:http://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( 
    <App/>
    , document.getElementById('root'))
  • 接入的方式如下:
    1. 从redux中导入createStore方法,这个用来将写好的reducer包装为store
    2. 从react-redux 中导入Provider组件,用来包裹我们应用的根组件<App/>。其原理是利用组件的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( 
      <Provider store={store}> 
        <App/> 
      </Provider>
, 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 (
      <div>
        <button onClick={this._onClick.bind(this)}>{this.props.counter}</button>
      </div>)
  }
  _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

ReactX
ReactX
2.5万字 · 7.5万阅读 · 22人关注
Web note ad 1