内容简介:写这篇文章是因为我所有能搜索到的文章都太!复!杂!了!,一上来就做了个todo list,并且使用了一大堆react-redux已经封装好的方法,所有的一切对我来说都是黑盒的,并且藕合度非常低,我根本不知道为什么这样写最后就会那样,有时候甚至这样写根本不能得到那样的结果,然而由于我并不知道中间发生了什么,所以只能去网上搜到底哪里出了错,往往还搜不到解决方案!!所以这里写了个最简单的例子,并且一步一步从原始写法到封装写法,以便理解封装那些方法的作用。redux的作用无需多言,因为react只负责view的部
写这篇文章是因为我所有能搜索到的文章都太!复!杂!了!,一上来就做了个todo list,并且使用了一大堆react-redux已经封装好的方法,所有的一切对我来说都是黑盒的,并且藕合度非常低,我根本不知道为什么这样写最后就会那样,有时候甚至这样写根本不能得到那样的结果,然而由于我并不知道中间发生了什么,所以只能去网上搜到底哪里出了错,往往还搜不到解决方案!!所以这里写了个最简单的例子,并且一步一步从原始写法到封装写法,以便理解封装那些方法的作用。
先搭建最简单的redux
redux的作用无需多言,因为react只负责view的部分,而不管理组件间的交互和时间传递,redux就是来解决这一问题的。
redux组成部分
创建一个redux需要先定义一些静态的东西,就好比我们现在要用乐高搭个变形金刚,先把你要的形状的乐高准备好。1.数据 2.动作 3.处理数据用的方法
1 . 创建初始state(数据)
state里你可以放任何你需要的数据,他就是个普通对象
const initialState={ name:'test', count:1 } 复制代码
2 . 创建actions (动作)
action描述要发起的动作类型,以及完成这个动作需要的参数,所以type是action必要的属性,其他的属性就随意定义你需要的,这里我定义了三个动作,加(addCount),减(minusCount),改变name(changeName)
//描述动作类型的常量 const ADD_COUNT='ADD_COUNT'; const MINUS_COUNT='MINUS_COUNT'; const CHANGE_NAME='CHANGE_NAME'; //actions //加动作 function addCount(count){ return { type:ADD_COUNT, count: count } } //减动作 function minusCount(count){ return { type:MINUS_COUNT, count:count } } //改变name function changeName(name){ return { type:'CHANGE_NAME', name:name } } 复制代码
3 . reducer (处理数据用的方法)
reducer处理action动作并返回新的state
//reducer function countReducer(state = initialState, action) { switch (action.type) { case ADD_COUNT: return Object.assign({},state,{count:state.count+action.count}); case MINUS_COUNT: return Object.assign({},state,{count:state.count-action.count}); case CHANGE_NAME return Object.assign({},state,{name:action.name}); default: return Object.assign({},state); } } 复制代码
现在,所有的乐高都准备好了,我们可以“组装”了。
4 .store(组装)
redux提供一个createStore方法,利用我们刚定义的state和reducer来生成一个store
import { createStore} from 'redux'; let store= createStore(countReducer,initialState); 复制代码
store提供四个方法dispach,subscribe,getState,replaceReducer 分别用来触发动作,订阅动作,获取当前的state,更换reducer。
5 . 结合react
react可以看成redux的使用者,他需要用redux来发起事件,订阅事件。
下面定义了两个组件App 和 Count。其中Count是App的子组件,在render中把store作为props传递给App,使得App可以使用store提供的方法.
import {render} from 'react-dom'; import {Component} from 'react'; class Count extends Component{ constructor(props){ super(props); } render(){ return( <div > Hello, Im a count: {this.props.count} </div> ); } } class App extends Component{ constructor(props){ super(props); this.state=this.props.store.getState(); } componentDidMount(){ let _this = this; let store=this.props.store //订阅store发起的所有事件,获取新的state用来更新自身的state store.subscribe(function(){ _this.setState(store.getState()); }); } add(count){ //发起addCount事件 this.props.store.dispatch(addCount(count)); } render(){ return ( <div> <Count store={this.store} count={this.state.count}/> <button onClick={this.add.bind(this,3)}>add</button> </div> ) } } let store= createStore(countReducer,initialState); render( <App store={store}/> , document.getElementById('container') ); 复制代码
到这里,我们的变形金刚已经可以动起来了,可以发起事件,也可以订阅事件并更新界面。不出意外的话,每次点击add页面上的数字都能加3了呢。
问题
但是现在还有些问题:
1.如果count的子组件需要使用store,我们得把store作为子组件的props层层传递下去
2.现在App可以通过store拿到state中所有的值,也就是state中有任何更新都会导致App重新渲染,但在实际项目中,一个react组件往往只需要state中的某些值。当然你可以在subscribe中拿到新的state后判断是否需要的属性发生了改变,然后再去更新界面来规避这个问题,现在,有个插件react-redux把这些都做好了。
redux-react提供了两个方法Provider,connect。Provider有一个必要的参数store,它使得所有通过connect生成的子组件能从props中获得store提供的方法。
首先改写App和render,将App作为Provider的子组件,并使用connect对原来的App进行改装
import {render} from 'react-dom'; import {Provider} from 'react-redux'; import {connect} from 'react-redux'; class App extends Component{ addCount(count){ //dispatch是被connect注入到props中的 this.props.dispatch(addCount(count)) } changeName(name){ this.props.dispatch(changeName(name)); } render(){ console.log('app render'); return ( <div> <Count count={this.props.count}/> <button onClick={this.addCount.bind(this,3)}>add</button> <button onClick={this.changeName.bind(this,'hello')}>changeName</button> </div> ) } } //把state中的值注入到组件的props中 function mapStateToProps(state){ return { //仅把count放到App的props中,在App中就可以使用this.props.count来访问count了 //并且只有当state中的count发生改变时才会引起app的重新渲染 count:state.count; } } //用connect生成的新组件覆盖原App,实际上我们在Provider里使用的是这个App,这一点非常重要 App=connect(mapStateToProps)(App); let store= createStore(countReducer,initialState); render( <Provider store={store}> <App/> </Provider>, document.getElementById('container') ) 复制代码
其中mapStateToProps方法以之前定义的state作为入参,返回值可以是整个state,也可以是你需要的部分数据,这里仅仅把count传递给了props,只有count值改变才会引起App的重新渲染,另外这个例子加了一个changeName的按钮,并在render方法里打了log来观察App的重新渲染,点击changeName导致了name值改变,但可以在调试窗口里看见并没有打印“app render”,说明name的改变并没有引起App的重新渲染。
通过connect我们已经能够在App中使用this.props.dispatch来发起事件了,但是我们并没有和一开始一样在App中写subscribe订阅事件,却依然能监听到addCount,这是因为connect把订阅事件也封装好了,它的源码是这样的:
Connect.prototype.componentDidMount = function componentDidMount() { this.trySubscribe(); }; 复制代码
trySubscribe中做了比较计算,只有被mapStateToProps映射到props上的值改变时,才会做setState操作来发起重新渲染。
另外,connect还提供了mapDispatchToProps方法把dispatch事件传递给props:
function mapDispatchToProps(dispatch){ return { addCount:function(count){ dispatch(addCount(count)); } } } App=connect(mapStateToProps,mapDispatchToProps)(App); 复制代码
这和在App中直接写add方法是一样的,这样我们就可以在App中使用onClick={this.props.addCount}来发起事件了,另外redux还提供一个方法bindActionCreators,把dispatch也给封装好了,所以上面的mapDispatchToProps还可以写成这样:
import {bindActionCreators} from 'redux'; function mapDispatchToProps(dispatch){ return { addCount:bindActionCreators(addCount,dispatch) } } 复制代码
这样看来bindActionCreators这个方法用处似乎不大,但是当你有一组事件都要放入组件的props时,用它就方便很多,你只要把所有定义好的action放在一个对象中传递给他就好了:
function mapDispachToProps(dispatch){ //actions是一个对象,里面包含了一组action return bindActionCreators(actions,dispatch); } 复制代码
bindActionCreators的源码非常简单,当actions是一个对象,里面包含一组动作,bindActionCreators就返回一个对象,类似这样:
{ addCount:function(){ return dispatch(actions.addCount.apply(undefined, arguments)) }, minusCount:function(){ return dispatch(actions.minusCount.apply(undefined, arguments)) } } 复制代码
当actions只是一个单独的动作,比如addCount,bindActionCreators的返回结果是一个方法:
function(){ return dispatch(addCount.apply(undefined, arguments)) } 复制代码
综上,这些看似高大上的插件只是把一些麻烦的方法给你封装好了,如果你不喜欢的话,不用也是完全没有问题的。
以上所述就是小编给大家介绍的《redux源码解读(简单易懂版)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Tangled Web
Michal Zalewski / No Starch Press / 2011-11-26 / USD 49.95
"Thorough and comprehensive coverage from one of the foremost experts in browser security." -Tavis Ormandy, Google Inc. Modern web applications are built on a tangle of technologies that have been de......一起来看看 《The Tangled Web》 这本书的介绍吧!