React实战-深入源码了解Redux用法之Connect

来源:互联网 发布:雅思自测软件 编辑:程序博客网 时间:2024/06/02 04:08

React实战-深入源码了解Redux用法之Connect

Redux结构并不复杂,用法也较之Flux更为简单,使得Redux成为ReactJS的标配。虽然我并不赞成用个windows就得搞清楚windows内存/线程怎么管的,用个算法函数就要知道算法怎么写的,但是如果不去看看Redux的源码,不先了解Flux的用法,在使用Redux的时候,你会感到迷惑。再者,Redux源码并不太多,但蕴含了巧妙的设计思想,使得我们在使用Redux编码时,更加简单,比Flux的代码量少了很多,另外我们常用的Redux函数并不太多,主要有Action,Reducers,store,provider,conner等(微信:react-javascript)。

即使之前用过Flux,在使用Redux时,也会有不明就理的感觉。因为Redux为我们做了很多事情。在Redux里面,我们很少用到ReactJs component的那些事件了,连componentDidMountcomponentUnMount这些主要的事件都少见了,还有涉及数据的更像操作与事件,涉及state的操作,storerequire('events').EventEmitter的事件绑定与触发等等都不知所踪,但是无论怎么变,这个数据流方式是不可改变的,这也就成为我们在学习和使用Redux过程中的雾水,让我们只知其然,不知其所以然了。

只能硬着头皮去看看Redux源码的处理方式,会让我们知道它的实现方式,写起代码来不再那么晕头晕脑了。首先是connect,为什么是它,因为这是一个将操作和数据与我们的componet进行关联的函数,成为理解Redux的一个至关重要和迷惑的函数,但是不知什么原因,在Redux的官网中并没有做为头号重心来介绍,甚至在主目录中都没有出现。

首先,看看在传统Rejeact component编码中的实现方式。

1.ReactJS + Flux中组件数据更新方式。

store中的事件如下:

 addChangedListener(callback)

  {

    this.on(CHANGE_EVENT, callback);

  },

  removeChangedListener(callback){

    this.removeListener(CHANGE_EVENT, callback);

  }

component中的事件与数据更新方式:

var PersionList= React.createClass({

  getInitialState: function () {

    return getPersions();

  },

  componentDidMount(){

    PersonStore.addChangedListener(this._onChange);

  },

  componentWillUnmount(){

    PersonStore.removeChangedListener(this._onChange);

  },

  onSearch()

  {

    PersonAction.searchByName(this.refs.organizeName.getValue());

  },

  _onChange(){

    this.setState(getPersons());

  },

  render()

  {

    return (

      <div >

        <div className="search">

          <TextField name="personName" ref="personName"/>

          <RaisedButton label="search" primary={true} onClick={this.onSearch}/>

        </div>

        <div className="list">

          <PersonList organizes={this.state.persons}/>

        </div>

      </div>

    );

  }

});

2.Redux中的实现方式

let AddTodo = ({ dispatch }) => {

  let input

  return (

    <div>

      <form onSubmit={e => {

        e.preventDefault()

        if (!input.value.trim()) {

          return

        }

        dispatch(addTodo(input.value))

        input.value = ''

      }}>

        <input ref={node => {

          input = node

        }} />

        <button type="submit">

          Add Todo

        </button>

      </form>

    </div>

  )

}

AddTodo = connect()(AddTodo)

以上可以看出采用connect方式省去了很多代码和操作,数据更新不需要自己主动触发了,事件处理不需要自己去绑定和解除绑定了。但是任何事都有利有弊,如同我们采用java编码,一上来就来个eclipse,自动生成一大堆的文件,顿时蒙了,不如自己写个helloworld.java清晰。

上面的connect更是莫名其妙了,数据、事件全没有,componnet怎么实现事件提交、数据更新的?!

3.Reduxconnect源码分析

还是看看一般的connect的写法吧。

function mapStateToProps(state) {

  return { todos: state.todos }

}

 

function mapDispatchToProps(dispatch) {

  return { actions: bindActionCreators(actionCreators, dispatch) }

}

 

export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)

扩展的参数就暂时不说了,主要是mapStateToPropsmapDispatchToProps

mapStateToProps:负责将state中的有用数据,外加组件自生的props传人关联组件。

mapDispatchToProps:主要负责传人组件关联的事件,这里暂时也不讲reduce事件了。

理解这两点,好像有点明白了,对于一个ReactJS组件,能获得组件的数据,绑定事件函数,似乎就足够了。但是问题来了,在传统方法中,我们还会绑定数据的更新操作,这里没有reactJSstate数据变化引起的组件重绘,更奇怪的是代码2connect参数为空?!

还是看看connect的源码实现吧:

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {

.......

return function wrapWithConnect(WrappedComponent) {

.......

}

}

以上是connect的函数主题结构。可以看出connect是采用了闭包的方式实现了两次操作。

a.第一次:实现对mapStateToProps, mapDispatchToProps, mergeProps, options = {}等输入参数的处理。

mapStateToProps的处理方式:

进一步往里看,我们可以到对mapStateToProps参数的处理

const defaultMapStateToProps = state => ({})

const mapState = mapStateToProps || defaultMapStateToProps

这里可以看出对mapStateToProps是有默认值处理的,如果mapStateToProps为空或者null,则默认为{}了。

mapDispatchToProps的处理方式:

const defaultMapDispatchToProps = dispatch => ({ dispatch })

 mapDispatch = defaultMapDispatchToProps

这里可以看出对mapDispatchToProps是有默认值处理的,如果mapDispatchToProps为空或者null,则默认为{ dispatch }了。

其它参数类似处理了,看到这里,我们至少明白了代码2中为什么为空了。

因为componet只是添加操作,不需要数据绑定,所有参数为{},事件为submit中执行dispatch(addTodo(input.value)),参数应为{dispatch}。如果采用完整的方式,应该为:

AddTodo = connect({}{dispatch})(AddTodo)才对。

而看看connect 源码知道,为null和不为null的结果是一样的。因此采用AddTodo = connect()(AddTodo)的方式了。

 

b.第二步才是真正建立commponent与数据源以及事件的关联。

在采用Redux的代码中我们看不到事件绑定与解除绑定,根本原因是connect已经我们的component自己生成了新的组件,已经不再是以前你定义的组件了,在新的组件中增加了事件操作等。看看connect源码:

return function wrapWithConnect(WrappedComponent) {

......

class Connect extends Component {

      shouldComponentUpdate() {

        return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged

      }

........

 componentDidMount() {

        this.trySubscribe()

      }

componentWillUnmount() {

        this.tryUnsubscribe()

        this.clearCache()

      }

}

}

从上面的代码可以看出,新创建的component中这些事件操作全了。

迷雾是不是少了很多,更多细节需要我们更加深入了解,跟随函数调用顺序,深入代码中,你会对框架的结构更加清楚。

0 0
原创粉丝点击