既然这个可能改变state的动作已经包装好了,那么我们怎么去判断并且对state做相应的改变呢?对,这就是reducer干的事情了。
reducer是state最终格式的确定。它是一个纯函数,也就是说,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
reducer对传入的action进行判断,然后返回一个通过判断后的state,这就是reducer的全部职责。如我们的counter应用:
import {INCREMENT_COUNTER,DECREMENT_COUNTER} from '../actions';
export default function counter(state = 0, action) {
switch (action.type){
case INCREMENT_COUNTER:
return state+1;
case DECREMENT_COUNTER:
return state-1;
default:
return state;
}
}
为什么需要redux
学过react的都知道,react用
state
和props
控制组件的渲染情况,而对于JavaScript单页面日趋复杂的今天,JavaScript需要管理越来越多的state,而这些state包括着各种乱七八糟途径来的数据。甚至有的应用的state会关系到另一个组件的状态。所以为了方便对这些state的管理以及对state变化的可控性。这个时候Redux这个东西就出来了,它可以让state的变化变得可预测。Redux的基本概念
什么是redux?这里非权威的解释:就是一个应用的state管理库,甚至可以说是前端数据库。更包括的是管理数据。
state
state是整个应用的数据,本质上是一个普通对象。 state决定了整个应用的组件如何渲染,渲染的结果是什么。可以说,State是应用的灵魂,组件是应用的肉体。 所以,在项目开发初期,设计一份健壮灵活的State尤其重要,对后续的开发有很大的帮助。 但是,并不是所有的数据都需要保存到state中,有些属于组件的数据是完全可以留给组件自身去维护的。
action
数据state已经有了,那么我们是如何实现管理这些state中的数据的呢?那就是action,什么是action?按字面意思解释就是动作,也可以理解成,一个可能!改变state的动作包装。就这么简单。。。。 只有当某一个动作发生的时候才能够触发这个state去改变,那么,触发state变化的原因那么多,比如这里的我们的点击事件,还有网络请求,页面进入,鼠标移入。。。所以action的出现,就是为了把这些操作所产生或者改变的数据从应用传到store中的有效载荷。 需要说明的是,action是state的唯一来源。它本质上就是一个JavaScript对象,但是约定的包含
type
属性,可以理解成每个人都要有名字一般。除了type属性,别的属性,都可以. 那么这么多action一个个手动创建必然不现实,一般我们会写好actionCreator
,即action的创建函数。调用actionCreator
,给你返回一个action。这里我们可以使用 redux-actions,嗯呢,我们下文有介绍。 比如有一个counter数量加减应用,我们就有两个action,一个decrement
,一个increment
。 所以这里的action creator写成如下:那么,当action创建完成了之后呢,我们怎么触发这些action呢,这时我们是要利用
dispatch
,比如我们执行count增减减少动作。为了减少样板代码,我们使用单独的模块或文件来定义 action type 常量
这么做不是必须的,在大型应用中把它们显式地定义成常量还是利大于弊的。
reducer
既然这个可能改变state的动作已经包装好了,那么我们怎么去判断并且对state做相应的改变呢?对,这就是reducer干的事情了。
reducer
是state最终格式的确定。它是一个纯函数,也就是说,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。reducer
对传入的action进行判断,然后返回一个通过判断后的state,这就是reducer的全部职责。如我们的counter应用:这里我们就是对增和减两个之前在action定义好的常量做了处理。 对于一个比较大一点的应用来说,我们是需要将reducer拆分的,最后通过redux提供的combineReducers方法组合到一起。 如此项目上的:
每个
reducer
只负责管理全局state
中它负责的一部分。每个reducer
的state
参数都不同,分别对应它管理的那部分state
数据。combineReducers()
所做的只是生成一个函数,这个函数来调用你的一系列reducer
,每个reducer
根据它们的key
来筛选出state
中的一部分数据并处理, 然后这个生成的函数再将所有reducer
的结果合并成一个大的对象。store
store是对之前说到一个联系和管理。具有如下职责
state
;getState()
方法获取 statedispatch(action)
方法更新 state;subscribe(listener)
注册监听器;subscribe(listener)
返回的函数注销监听器。 强调一下 Redux 应用只有一个单一的store
。当需要拆分数据处理逻辑时,你应该使用reducer
组合,而不是创建多个store
。store
的创建通过redux
的createStore
方法创建,这个方法还需要传入reducer
,很容易理解:毕竟我需要dispatch
一个action
来改变state
嘛。 应用一般会有一个初始化的state
,所以可选为第二个参数,这个参数通常是有服务端提供的,传说中的Universal
渲染。后面会说。。。 第三个参数一般是需要使用的中间件,通过applyMiddleware传入。 说了这么多,action
,store
,actionCreator
,reducer
关系就是这么如下的简单明了:结合react-redux的使用
react-redux
,redux
和react
的桥梁工具。react-redux
将组建分成了两大类,UI组建component
和容器组建container
。 简单的说,UI组建负责美的呈现,容器组件负责来帮你盛着,给你"力量"。 UI 组件有以下几个特征:容器组件特性则恰恰相反:
export default connect( state=>({ counter: state.counter }), ActionCreators )(App);
这里我们的
createCreator
返回的是一个fetch
对象,我们下文会介绍,我们通过dispatch
触发改action
在请求数据之前,通过
redux-thunk
我们可以先触发加载中的action
,等请求数据结束之后我们可以在次触发action
,使得加载中状态取消,并处理请求结果。redux-promise
既然说到了异步
action
,我们可以使用redux-promise
,它可以让actionCreator
返回一个Promise
对象。 第一种做法,我们可以参考redux-thunk
的部分。 第二种做法,action
对象的payload
属性(相当于我们的diy参数,action里面携带的其他参数)是一个Promise
对象。这需要从redux-actions
模块引入createAction
方法,并且写法也要变成下面这样。其实
redux-actions
的createAction
的源码是拿到fetch对象的payload结果之后又触发了一次action
。redux-actions
当我们的在开发大型应用的时候,对于大量的
action
,我们的reducer
需要些大量的swich来对action.type
进行判断。redux-actions
可以简化这一烦琐的过程,它可以是actionCreator
,也可以用来生成reducer
,其作用都是用来简化action
、reducer
。 主要函数有createAction
、createActions
、handleAction
、handleActions
、combineActions
。createAction
创建
action
,参数如下例子如下:
createActions
创建多个
action
。第一个参数
actionMap
为一个对象,以action type
为键值,值value有三种形式,action
创建的时候传入的参数,返回结果会作为到生成的action
的payload
的value。payload
的值,第二个值也为一个函数,返回meta
的值,不知道有什么用。actionMap
对象,递归作用吧。 例子如下expect(actionCreators.app.counter.increment(1)).to.deep.equal({ type: 'APP/COUNTER/INCREMENT', payload: { amount: 1 }, meta: { key: 'value', amount: 1 } }); expect(actionCreators.app.counter.decrement(1)).to.deep.equal({ type: 'APP/COUNTER/DECREMENT', payload: { amount: -1 } }); expect(actionCreators.app.counter.set(100)).to.deep.equal({ type: 'APP/COUNTER/SET', payload: 100 }); expect(actionCreators.app.notify('yangmillstheory', 'Hello World')).to.deep.equal({ type: 'APP/NOTIFY', payload: { message: 'yangmillstheory: Hello World' }, meta: { username: 'yangmillstheory', message: 'Hello World' } });
handleAction
字面意思理解,处理
action
,那就是一个reducer
,包裹返回一个reducer
,处理一种类型的action type
。当第二个参数为一个reducer处理函数时,形式如下,处理传入的
state
并返回新的state
:当第二个参数为reducerMap时,也为处理
state
并返回新的state
,只是必须传入key值为next
和throw
的两个函数,分别用来处理state和异常如下:官方推荐使用
reducerMap
形式,因为与ES6的generator
类似。handleActions
与
handleAction
不同,handleActions
可以处理多个action
,也返回一个reducer
。reducerMap
以action type
为key,value与handleAction
的第二个参数一致,传入一个reducer
处理函数或者一个只有next
和throw
两个键值的对象。 另外,键值key也可以使用createAction
创建:combineActions
将多个
action
或者actionCreator
结合起来,看起来很少用,具体例子如下:redux-actions
说到这里,大概是这样,有什么不了解看看官方文档吧。reselect
Reselect
用来记忆selectors
的库,我们定义的selectors
是作为函数获取state
的某一部分。使用记忆能力,我们可以组织不必要的衍生数据的重渲染和计算过程,由此加速了我们的应用。具体细节大概是在mapStateToProps
的时候,讲state
的某一部分交给reselect
的selectors
来管理,使用selectors
的记忆功能让组件的props
尽量不变化,引起不必要的渲染。 下面我们以一个todolist为例子。 当我们没有reselect
的时候,我们是直接通过mapStateToProps
把数据传入组件内,如下。这个代码有一个潜在的问题。每当
state tree
改变时,selector
都要重新运行。当state tree
特别大,或者selector
计算特别耗时,那么这将带来严重的运行效率问题。为了解决这个问题,reselect为selector
设置了缓存,只有当selector
的输入改变时,程序才重新调用selector函数。 这时我们把state
转化为props
的数据交给reselect来
处理,我们重写mapStateToProps
。我们使用
createSelector
包裹起来,将组件内需要的两个props
包裹起来,然后在返回一个获取数据的函数getVisibleTodos
,这样返回的todolist
就不会受到一些不必要的state的变化而变化引起冲渲染。最后
总结了那么多的用法,其实也是
redux
的基本用法,然后自己写了半天的todolist
,把上面说到的技术都用了,这是 github地址,上面的内容如有错误,勿喷,毕竟入门级别。。。