一.什么是Redux?
Redux是最近很火的一个前端数据流框架,之前接触过Flux应该很熟悉了。Redux可以看作是Flux的一种实现。Redux并不是React的一部分,它们没有任何关系。对于单页面运用来说,Redux是利器。其实对于Redux,官方文档已经非常详细。本文主要讲解React以及RN中如何使用Redux。下面我们就来一点一点揭开Redux的神秘面纱。
二.三大原则
单一数据源
Redux会把数据储存在一个object tree中,并且这个object tree只存在于一个单一store中。也就是说,Redux会把所有的数据存到一个单例里面,然后我们各自组件根据自己的需要按需来获取。这点与Flux不同,Flux存在着许许多多的store。
state只读
如果想改变store里面的state,只能通过发送action,没有第二种方式。这和Flux是一样的。在Redux和Flux中都是单向数据流。
纯函数执行修改
使用reducer来改变state tree。reducer只能是纯函数,不能进行任何副操作。输入相同输出也必定相同。
三.元素
Action
和Flux一样,action是一个纯对象,它接收一个数据返回一个带有type字段的对象,形如下:
{
type:RECEIVE_DATA,
data:'data'
}
我们定义一个只读变量RECEIVE_DATA(一般大写)来表明action的type,然后用一个data来储存传过来的数据。在你想要改变数据的时候(比如网络请求),我们会通过某种方式将action中的数据传到store。
Action Creator
action通过action creator创建,action creator是一个纯函数。它接收一些参数(比如json数据),生成相应的action对象。形如下:
function Receive_data(data){
return{
type:RECEIVE_DATA,
data:data
}
}
在这里我们简单返回一个带type和data的action。
在Flux中,action creator需要做dispatch操作,行如下:
function Receive_data(data){
dispatch({
type:RECEIVE_DATA,
data:data
})
}
dispatch是一个用来发送action的函数,它会把action传递到redux。而在redux中,我们需要自己发起dispatch动作。
Reducer
Redux中,reducer是一个纯函数。它接收两个参数,一个原来的state和一个发起的action。当我们执行dispatch(action)之后,会把action传入reducer。由于Redux中state是唯一的,reducer会根据传入的action.type判断传过来的是哪一个action,然后根据action所携带的data更新state的某一部分。
要注意,由于state是可读的,我们并不能直接对state进行操作。这里我们使用Object.assign和ES7对象展开语法对原来的state进行拷贝,并根据action修改其相对应的部分。最后返回一个全新的state对象。
对于Flux,并不存在reducer,取而代之的是一个叫Dispatcher的东西,在这里我们主要讲Redux的相关内容,只是拿Flux来作对比,就不过多赘述Flux的相关内容。
Stroe
Redux中,store存储着单一state。可以把store看成一个单例对象,每次dispatch(action)之后,在reducer里生成新的state对象,最后用新的state对象替换掉原来在store里的旧state对象。store通过reducer创建。reducer不是唯一的,有时候我们为了拆分数据处理逻辑,会创建多个reducer,我们可以通过redux为我们提供的combineReducers来合并多个reducer。一般我们会这么写
这样我们就把两个reducer合并在了一起,然后我们导出reduces,在你需要创建store的地方使用reduces来创建
import { createStore } from 'redux'
let store = createStore(reduces)
我们可以调用store.getState(),拿到我们的state。
console.log('store',store.getState())
//打印出来为
{
textChange:{Text:[]}
todoApp:{todos:[]}
}
可以看到combineReducers会把传入的reducer的方法名生成state中数据对应的key,我们取数据的时候得像state.textChange.Text这样来取。
Flux中的store并不是单一的。在Flux中,store需要自己实现数据处理函数,并在合并成功以后主动发送事件更新UI。Flux的store看起来像这样的
var ListStore=assign({},EventEmitter.prototype,{
items:[],
getAll:function(){
returnthis.items;
},
addNewItemHandler:function(text){
this.items.push(text);
},
emitChange:function(){
this.emit('change');
},
addChangeListener:function(callback){
this.on('change',callback);
},
removeChangeListener:function(callback){
this.removeListener('change',callback);
}
AppDispatcher.register函数在接收到action的时候,会根据action.type去调用对应的store。而Redux则没有这么繁琐,简化了很多东西。
四.搭配React-Native
使用
之前我们根据reducers创建了store实例,理论上来说我们可以通过store.dispatch(action)去更新数据,但真正在RN中,我们不直接操作store。Redux给我们提供了Provider和connect帮助我们连接RN。
import{ Provider ,connect} from'react-redux'
首先我们需要提供一个select函数作为connect一个参数。select会接收到一个全局state,通过自身组件的情况,select返回自身组件中需要用到的props;connect的第二个参数为你需要连接到Redux的Component。调用connect之后,connect会把select筛选后的state和dispatch传入我们自己的组件中。
然后我们用Provider作为顶层组件,把刚刚创建好的store传入Provider的store属性,并把连接后的组件AppConnect包在Provider里面。注意:Provider只能有一个子组件。
Provider在这里的作用就是将我们创建的store传递给connect包装之后的组件(把store.getState()传入select函数),然后组件根据select拿走自己需要的数据。AppConnect就是包装后的SimpleApp。在SimpleApp中,我们可以直接用this.props.dispatch(action)发起action和this.props.Text拿到我们的数据。
数据流
Redux的数据流是单向的,如图:
这是同步action的情况。这里的Smart Component就类似于Provider。Smart Component存储着整个store。Smart Component会把dispatch作为默认props传给子组件。dumb Component会根据select函数选择自己需要的props。
示例
说了太多,不如实践。下面带大家做一个添加删除列表的小程序。
实现一个ListView,当我们点击点我的时候,向ListView的dataSource里面插入一个字符串。点击字符串的时候,把它从列表里面移除。很简单,现在我们用Redux实现。
首先我们要创建两个action,一个添加action,一个删除action。创建一个js file命名action
action.jstype是必须的,addTextAction的message用于保存我们想要传入state的数据。deleteTextAction保存我要删除数据的索引。
然后我们来创建reducer,新建一个js file命名reducer。在这里我们的reducer需要处理两个action,但是并不需要逻辑分离,所以:
reducer.js之前说过使用combineReducers是为了合并多个reducer,并把传入的key作为state中数据所对应的key,建议这么来写。在这里我们把数据在原state的基础上进行了拼接和删除,并返回一个新的state。
最后我们照之前的说的,根据reduces生成store,并把store和redux连接起来。连接方法前面又讲,这里只展示组件。
index.ios.js在点击点我按钮的时候,我们发送addTextAction,并传入WSD。这是数据就会顺着action,来到reducer,再来到store,最后子组件的props改变,重新渲染。
Cell.js在ListView的每一个Cell点击事件中,我们发送deleteTextAction来固定删除index为1的元素。当然正常你应该传入rowId,并根据rowId来删除相关的元素,这里我们简单处理。
五.异步Action
Redux本身可以很好的处理同步事件,但其实大多数时候,我们都需要处理一些异步的事情。比如网络数据解析。Redux为我们提供了一些中间件来处理各种复杂的情况。
Middleware说明
关于Redux的middleware,官方给出了两种实现。一种是使用新的函数链式替换原本的dispatch。比如
类似于循环替换store,每一次传入middleware的store都是之前middleware包装过的store。
第二种是利用函数柯理化,这种类似于官方实现。
myMiddleware.js我们利用箭头函数使其柯理化,首先把原始的store.dispatch赋值给dispatch变量,然后遍历传入的middlewares。在循环中,把store和dispatch传入,最后返回一个包装过的函数action=>{function(){...}},再赋予dispatch,然后再把新的dispatch传入下一个中间件。
当然这并不是官方完整的实现,具体区别请看Redux官方文档middleWare一节。
异步
我们需要引入thunkMiddleware来执行异步操作,并把store用中间件包起来。
import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware } from 'redux'
let store = applyMiddleware(thunkMiddleware)(createStore)(reduces)
thunkMiddleware的具体做法是根据dispatch传递进来的action,判断如果该action是一个方法,并且方法返回的是一个Promise对象,就会传入dispatch并且执行异步操作。真正把action传递到reducer是在异步action返回的方法里面。
这是一个最简单的异步action。在我们需要请求网络的地方,使用dispatch(fetchPosts('www.xxxxxxx.com')),最后在执行完异步操作之后别忘了调用dispatch去发起一个普通action,真正将数据传递到store。
异步数据流
从Actions->API Wrapper->API Service->API Wrapper->Actions这一步都是thunk帮我们做的事情。完成之后我们只需要像以前一样正正常常的发送我们的同步action就OK了。
最后,我们还是一如既往的奉上公众号二维码,喜欢就关注吧,我们会不定期推送相关技术文章,谢谢支持。
网友评论