您的位置:首页 > Web前端 > React

react-redux高阶组件connect方法使用介绍以及实现原理

2018-01-26 20:38 1596 查看

redux



connect
之前先来回顾一下
redux
的基本用法, 见下面的例子:

import { createStore } from 'redux';

function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}

// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
console.log(store.getState())
);

// 改变内部 state 惟一方法是 dispatch 一个 action。
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1


以上总结起来就只有下面四个方法

let store = createStore(reducer); // 创建store
store.getState( ); // 获取state值
store.dispatch({ type: "text" }); // 使用action更改在reducer中定义好的更改store的更改策略
store.subScribe(render); // 设置监听函数, 在更改store之后触发


关于
redux
的更多内容, 可以参阅官方文档.

好了, 进入正题

react-redux 高阶组件 connect方法介绍以及实现原理

在使用介绍
connect
之前, 先简单介绍一下什么是高阶组件.

高阶组件

高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。 实际上就是一个类工厂, 见下面的一个例子.

import React, { Component } from 'react'

export default (People) => {
class Star extends Component {
// 可以做很多自定义逻辑
render () {
return <People/>
}
}
return Star
}


这就好比一个普通的人, 经过公司的包装之后, 变成一个会很多种技能的明星一样.

import React, { Component } from 'react'

export default (People, things) => {
class Star extends Component {
constructor () {
super()
this.state = { data: null }
}

componentWillMount () {
ajax.get('/data/' + things, (data) => {
this.setState({ data })
})
}

render () {
return <People data={this.state.data} />
}
}
return Star
}


People
推上市场之前, 先告诉公司他需要什么
things
, 然后公司在
componentWillMount
阶段对他需要的
things
进行准备, 最后返回一个
Star


React.js 的 context

还要再说一个小概念, 如果不提这个无法讲述connect的实现原理, 因此如果不想看原理只想看使用方法的话, 这一部分可以跳过, 不过博主会用简单的语言描述这个问题, 最好看一下.ヾ(◍°∇°◍)ノ゙

某个组件只要往自己的
context
里面放了某些状态,这个组件之下的所有子组件都直接访问这个状态而不需要通过中间组件的传递。一个组件的
context
只有它的子组件能够访问,它的父组件是不能访问到的,你可以理解每个组件的
context
就是瀑布的源头,只能往下流不能往上飞。

那么怎么设置
context
呢? 见下面的代码

class Parent extends Component { // 父组件
static childContextTypes = {
tags: PropTypes.string
}

constructor () {
super()
this.state = { tags: 'hello' }
}

getChildContext () {
return { index: this.state.tags }
}

render () {
return (
<div>
<Title />
</div>
)
}
}

class Title extends Component {
static contextTypes = {
tags: PropTypes.string
}

render () {
return (
<h1>{ this.context.tags }</h1>
)
}
}


总的来说:

一个组件可以通过
getChildContext
方法返回一个对象, 这个对象就是子树的
context
, 提供
context
的组件必须提供
childContextTypes
作为
context
的声明和验证.

如果一个组件设置了
context
, 那么它的子组件都可以直接访问到里面的内容, 它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过
contextTypes
来声明你想要的
context
里面的哪些状态, 然后可以通过
this.context
访问到那些状态.

connect

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])


connect
有四个参数, 但是后两个参数用到的很少, 所以本篇博客之探讨前两个参数.

connect 的第一个参数是 mapStateToProps

这个函数允许我们将 store 中的数据作为 props 绑定到组件上

const mapStateToProps = (state) => { // 正常我们在react-redux中会这样书写
return {
themeColor: state.themeColor
}
}
People = connect(mapStateToProps)(People) // connect返回来的是一个函数, 因此还要再一次调用传入组件


实现主要原理, 就是将需要绑定的
props
作为一个函数传过来, 在
connect
中传给
mapStateToProps
一个真实的
store
的数据

const connect = (mapStateToProps) => (People) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object
}

constructor () {
super()
this.state = { allProps: {} }
}

componentWillMount () {
const { store } = this.context
this.setProps()
}

setProps () {
const { store } = this.context
let stateProps = mapStateToProps(store.getState(), this.props) // 额外传入 props
this.setState({
allProps: { // 整合普通的 props 和从 state 生成的 props
...stateProps,
...this.props
}
})
}

render () {
return <People {...this.state.allProps} />
}
}
return Connect
}


connect 的第二个参数是 mapDispatchToProps

由于更改数据必须要触发
action
, 因此在这里的主要功能是将
action
作为
props
绑定到 组件上

const mapDispatchToProps = (dispatch, ownProps) => {
return {
increase: (...args) => dispatch(actions.increase(...args)),
decrease: (...args) => dispatch(actions.decrease(...args))
}
}

class People extends Component {
render(){
const {count, increase, decrease} = this.props;
return (<div>
<div>计数:{this.props.count}次</div>
<button onClick={increase}>增加</button>
<button onClick={decrease}>减少</button>
</div>)
}
}

const NiuPeople = connect(mapStateToProps, mapDispatchToProps)(People);


这里的实现原理和上面的相差不多, 主要是将
action
props
一起传到组件里.

const connect = (mapStateToProps, mapDispatchToProps) => (People) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object
}

constructor () {
super()
this.state = {
allProps: {}
}
}

componentWillMount () {
const { store } = this.context
this.setProps()
store.subscribe(() => this.setProps())
}

setProps () { // 做了一下完整性的判断
const { store } = this.context
let stateProps = mapStateToProps
? mapStateToProps(store.getState(), this.props)
: {} // 防止 mapStateToProps 没有传入
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props)
: {} // 防止 mapDispatchToProps 没有传入
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}

render () {
return <People {...this.state.allProps} />
}
}
return Connect
}


Provider

这是最后一个要说的问题, 讲到这里可能有一个疑问, 就是context是什么时候设置的呢. 下面要说的就是这个问题.

Provider
就是
react-redux
中的一个组件,
Provider
做的事情也简单, 它就是一个容器组件, 会把嵌套的内容原封不动作为自己的子组件渲染出来. 它还会把外界传给它的
props.store
放到
context
, 这样子组件
connect
的时候都可以获取到. 见下面代码.

class Provider extends Component {
static propTypes = {
store: PropTypes.object,
children: PropTypes.any
}

static childContextTypes = {
store: PropTypes.object
}

getChildContext () {
return {
store: this.props.store
}
}

render () {
return (
<div>{this.props.children}</div>
)
}
}


参考地址:

作者: 胡子大哈 http://huziketang.com/books/react/lesson40

作者: 沈斯明 http://blog.csdn.net/u010977147/article/details/53412381
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐