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

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

2016-09-09 23:26 676 查看
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的那些事件了,连componentDidMount、componentUnMount这些主要的事件都少见了,还有涉及数据的更像操作与事件,涉及state的操作,store中require('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.Redux中connect源码分析
还是看看一般的connect的写法吧。
function mapStateToProps(state) {
  return { todos: state.todos }
}
 
function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators(actionCreators, dispatch) }
}
 
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
扩展的参数就暂时不说了,主要是mapStateToProps,mapDispatchToProps。
mapStateToProps:负责将state中的有用数据,外加组件自生的props传人关联组件。
mapDispatchToProps:主要负责传人组件关联的事件,这里暂时也不讲reduce事件了。
理解这两点,好像有点明白了,对于一个ReactJS组件,能获得组件的数据,绑定事件函数,似乎就足够了。但是问题来了,在传统方法中,我们还会绑定数据的更新操作,这里没有reactJS的state数据变化引起的组件重绘,更奇怪的是代码2中connect参数为空?!
还是看看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中这些事件操作全了。
迷雾是不是少了很多,更多细节需要我们更加深入了解,跟随函数调用顺序,深入代码中,你会对框架的结构更加清楚。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息