redux源码解读(简单易懂版)

栏目: IOS · Android · 发布时间: 6年前

内容简介:写这篇文章是因为我所有能搜索到的文章都太!复!杂!了!,一上来就做了个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源码解读(简单易懂版)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Agile Web Development with Rails 4

Agile Web Development with Rails 4

Sam Ruby、Dave Thomas、David Heinemeier Hansson / Pragmatic Bookshelf / 2013-10-11 / USD 43.95

Ruby on Rails helps you produce high-quality, beautiful-looking web applications quickly. You concentrate on creating the application, and Rails takes care of the details. Tens of thousands of deve......一起来看看 《Agile Web Development with Rails 4》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具