学过react的都知道,react用state
和props
控制组件的渲染状况,而对于JavaScript单页面日趋复杂的今天,JavaScript须要管理愈来愈多的state,而这些state包括着各类乱七八糟途径来的数据。甚至有的应用的state会关系到另外一个组件的状态。因此为了方便对这些state的管理以及对state变化的可控性。这个时候Redux这个东西就出来了,它可让state的变化变得可预测。前端
什么是redux?这里非权威的解释:就是一个应用的state管理库,甚至能够说是前端数据库。更包括的是管理数据。react
state是整个应用的数据,本质上是一个普通对象。
state决定了整个应用的组件如何渲染,渲染的结果是什么。能够说,State是应用的灵魂,组件是应用的肉体。
因此,在项目开发初期,设计一份健壮灵活的State尤为重要,对后续的开发有很大的帮助。
可是,并非全部的数据都须要保存到state中,有些属于组件的数据是彻底能够留给组件自身去维护的。git
数据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写成以下:github
export function decrement() { return{ type:DECREMENT_COUNTER } } export function increment(){ return{ type:INCREMENT_COUNTER } }
那么,当action建立完成了以后呢,咱们怎么触发这些action呢,这时咱们是要利用dispatch
,好比咱们执行count增减减小动做。数据库
export function incrementIfOdd(){ return(dispatch,getState)=>{ const {counter} = getState(); if(counter%2==0) { return; } dispatch(increment()); } } export function incrementAsync() { return dispatch => { setTimeout(() => { dispatch(increment()); }, 1000); }; }
为了减小样板代码,咱们使用单独的模块或文件来定义 action type 常量npm
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
这么作不是必须的,在大型应用中把它们显式地定义成常量仍是利大于弊的。json
既然这个可能改变state的动做已经包装好了,那么咱们怎么去判断而且对state作相应的改变呢?对,这就是reducer干的事情了。reducer
是state最终格式的肯定。它是一个纯函数,也就是说,只要传入参数相同,返回计算获得的下一个 state 就必定相同。没有特殊状况、没有反作用,没有 API 请求、没有变量修改,单纯执行计算。reducer
对传入的action进行判断,而后返回一个经过判断后的state,这就是reducer的所有职责。如咱们的counter应用:redux
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; } }
这里咱们就是对增和减两个以前在action定义好的常量作了处理。
对于一个比较大一点的应用来讲,咱们是须要将reducer拆分的,最后经过redux提供的combineReducers方法组合到一块儿。 如此项目上的:数组
const rootReducer = combineReducers({ counter }); export default rootReducer;
每一个reducer
只负责管理全局state
中它负责的一部分。每一个reducer
的state
参数都不一样,分别对应它管理的那部分state
数据。combineReducers()
所作的只是生成一个函数,这个函数来调用你的一系列reducer
,每一个reducer
根据它们的key
来筛选出state
中的一部分数据并处理, 而后这个生成的函数再将全部reducer
的结果合并成一个大的对象。promise
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
,redux
和react
的桥梁工具。react-redux
将组建分红了两大类,UI组建component
和容器组建container
。 简单的说,UI组建负责美的呈现,容器组件负责来帮你盛着,给你"力量"。
UI 组件有如下几个特征:
如:
export default class Counter extends Component{ render(){ const { counter, increment, decrement, incrementIfOdd, incrementAsync } = this.props; return( <p> Clicked:{counter} times <button onClick={increment}>+</button> <button onClick={decrement}>-</button> <button onClick={incrementIfOdd}>increment if Odd</button> <button onClick={incrementAsync}>increment async</button> </p> ) } }
容器组件特性则偏偏相反:
class App extends Component{ render(){ const { counter, increment, decrement, incrementIfOdd, incrementAsync } = this.props; return( <Counter counter={counter} increment={increment} decrement={decrement} incrementIfOdd={incrementIfOdd} incrementAsync={incrementAsync}/> ) } } export default connect( state=>({ counter: state.counter }), ActionCreators )(App);
connect
方法接受两个参数:mapStateToProps
和mapDispatchToProps
。它们定义了UI组件的业务逻辑。前者负责输入逻辑,即将state
映射到 UI 组件的参数(props), 后者负责输出逻辑,即将用户对 UI 组件的操做映射成Action
。由于做为组件,咱们只要能拿到值,能发出改变值得action就能够了,因此mapStateToProps
和mapDispatchToProps
正是知足这个需求的。
一个比较流行的redux的action中间件,它可让actionCreator
暂时不返回action
对象,而是返回一个函数,函数传递两个参数(dispatch, getState)
,在函数体内进行业务逻辑的封装,好比异步操做,咱们至少须要触发两个action
,这时候咱们能够经过redux-thunk
将这两个action
封装在一块儿,以下:
const fetchDataAction = (querys) => (dispatch, getState) => { const setLoading = createAction('SET_LOADING'); dispatch(setLoading(true)); // 设置加载中。。。 return fetch(`${url}?${querys}`).then(r => r.json()).then(res => { dispatch(setLoading(false)); // 设置取消加载中。。。 dispatch(createAction('DATA_DO_SOMETHIN')(res)) }) }
这里咱们的createCreator
返回的是一个fetch
对象,咱们下文会介绍,咱们经过dispatch
触发改action
dispatch(fetchDataAction(querys))
在请求数据以前,经过redux-thunk
咱们能够先触发加载中的action
,等请求数据结束以后咱们能够在次触发action
,使得加载中状态取消,并处理请求结果。
既然说到了异步action
,咱们可使用redux-promise
,它可让actionCreator
返回一个Promise
对象。
第一种作法,咱们能够参考redux-thunk
的部分。
第二种作法,action
对象的payload
属性(至关于咱们的diy参数,action里面携带的其余参数)是一个Promise
对象。这须要从redux-actions
模块引入createAction
方法,而且写法也要变成下面这样。
import { createAction } from 'redux-actions'; class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props // 发出异步 Action dispatch(createAction( 'FETCH_DATA', fetch(`url`).then(res => res.json()) )); }
其实redux-actions
的createAction
的源码是拿到fetch对象的payload结果以后又触发了一次action
。
当咱们的在开发大型应用的时候,对于大量的action
,咱们的reducer
须要些大量的swich来对action.type
进行判断。redux-actions
能够简化这一烦琐的过程,它能够是actionCreator
,也能够用来生成reducer
,其做用都是用来简化action
、reducer
。
主要函数有createAction
、createActions
、handleAction
、handleActions
、combineActions
。
建立action
,参数以下
import { createAction } from 'redux-actions'; createAction( type, // action类型 payloadCreator = Identity, // payload数据 具体参考Flux教程 ?metaCreator // 具体我也没深究是啥 )
例子以下:
export const increment = createAction('INCREMENT') export const decrement = createAction('DECREMENT') increment() // { type: 'INCREMENT' } decrement() // { type: 'DECREMENT' } increment(10) // { type: 'INCREMENT', payload: 10 } decrement([1, 42]) // { type: 'DECREMENT', payload: [1, 42] }
建立多个action
。
import { createActions } from 'redux-actions'; createActions( actionMap, ?...identityActions, )
第一个参数actionMap
为一个对象,以action type
为键值,值value有三种形式,
action
建立的时候传入的参数,返回结果会做为到生成的action
的payload
的value。payload
的值,第二个值也为一个函数,返回meta
的值,不知道有什么用。actionMap
对象,递归做用吧。例子以下
createActions({ ADD_TODO: todo => ({ todo }) REMOVE_TODO: [ todo => ({ todo }), // payloa (todo, warn) => ({ todo, warn }) // meta ] });
const actionCreators = createActions({ APP: { COUNTER: { INCREMENT: [ amount => ({ amount }), amount => ({ key: 'value', amount }) ], DECREMENT: amount => ({ amount: -amount }), SET: undefined // given undefined, the identity function will be used }, NOTIFY: [ (username, message) => ({ message: `${username}: ${message}` }), (username, message) => ({ username, message }) ] } }); 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' } });
第二个参数identityActions
,可选参数,也是一个action type
吧,官方例子没看懂,以下:
const { actionOne, actionTwo, actionThree } = createActions({ // function form; payload creator defined inline ACTION_ONE: (key, value) => ({ [key]: value }), // array form ACTION_TWO: [ (first) => [first], // payload (first, second) => ({ second }) // meta ], // trailing action type string form; payload creator is the identity }, 'ACTION_THREE'); expect(actionOne('key', 1)).to.deep.equal({ type: 'ACTION_ONE', payload: { key: 1 } }); expect(actionTwo('first', 'second')).to.deep.equal({ type: 'ACTION_TWO', payload: ['first'], meta: { second: 'second' } }); expect(actionThree(3)).to.deep.equal({ type: 'ACTION_THREE', payload: 3, });
字面意思理解,处理action
,那就是一个reducer
,包裹返回一个reducer
,处理一种类型的action type
。
import { handleAction } from 'redux-actions'; handleAction( type, // action类型 reducer | reducerMap = Identity defaultState // 默认state )
当第二个参数为一个reducer处理函数时,形式以下,处理传入的state
并返回新的state
:
handleAction('APP/COUNTER/INCREMENT', (state, action) => ({ counter: state.counter + action.payload.amount, }), defaultState);
当第二个参数为reducerMap时,也为处理state
并返回新的state
,只是必须传入key值为next
和throw
的两个函数,分别用来处理state和异常以下:
handleAction('FETCH_DATA', { next(state, action) {...}, throw(state, action) {...}, }, defaultState);
官方推荐使用reducerMap
形式,由于与ES6的generator
相似。
与handleAction
不一样,handleActions
能够处理多个action
,也返回一个reducer
。
import { handleActions } from 'redux-actions'; handleActions( reducerMap, defaultState )
reducerMap
以action type
为key,value与handleAction
的第二个参数一致,传入一个reducer
处理函数或者一个只有next
和throw
两个键值的对象。
另外,键值key也可使用createAction
建立:
import { createActions, handleActions } from 'redux-actions'; const { increment, decrement } = createActions({ 'INCREMENT': amount => ({ amount: 1 }), 'DECREMENT': amount => ({ amount: -1 }) }); const reducer = handleActions({ [increment](state, { payload: { amount } }) { return { counter: state.counter + amount } }, [decrement](state, { payload: { amount } }) { return { counter: state.counter + amount } } }, defaultState);
将多个action
或者actionCreator
结合起来,看起来不多用,具体例子以下:
const { increment, decrement } = createActions({ INCREMENT: amount => ({ amount }), DECREMENT: amount => ({ amount: -amount }) }); const reducer = handleActions({ [combineActions(increment, decrement)](state, { payload: { amount } }) { return { ...state, counter: state.counter + amount }; } }, { counter: 10 }); expect(reducer({ counter: 5 }, increment(5))).to.deep.equal({ counter: 10 }); expect(reducer({ counter: 5 }, decrement(5))).to.deep.equal({ counter: 0 }); expect(reducer({ counter: 5 }, { type: 'NOT_TYPE', payload: 1000 })).to.equal({ counter: 5 }); expect(reducer(undefined, increment(5))).to.deep.equal({ counter: 15 });
redux-actions
说到这里,大概是这样,有什么不了解看看官方文档吧。
Reselect
用来记忆selectors
的库,咱们定义的selectors
是做为函数获取state
的某一部分。使用记忆能力,咱们能够组织没必要要的衍生数据的重渲染和计算过程,由此加速了咱们的应用。具体细节大概是在mapStateToProps
的时候,讲state
的某一部分交给reselect
的selectors
来管理,使用selectors
的记忆功能让组件的props
尽可能不变化,引发没必要要的渲染。
下面咱们以一个todolist为例子。
当咱们没有reselect
的时候,咱们是直接经过mapStateToProps
把数据传入组件内,以下。
const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } const mapStateToProps = (state, props) => { return { todolist: getVisibleTodos(state, props) } }
这个代码有一个潜在的问题。每当state tree
改变时,selector
都要从新运行。当state tree
特别大,或者selector
计算特别耗时,那么这将带来严重的运行效率问题。为了解决这个问题,reselect为selector
设置了缓存,只有当selector
的输入改变时,程序才从新调用selector函数。
这时咱们把state
转化为props
的数据交给reselect来
处理,咱们重写mapStateToProps
。
const getVisibilityFilter = state => state.todo.showStatus const getTodos = state => state.todo.todolist const getVisibleTodos = createSelector([getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } }) const mapStateToProps = (state, props) => { const todolist = getVisibleTodos(state, props); return { todolist } }
咱们使用createSelector
包裹起来,将组件内须要的两个props
包裹起来,而后在返回一个获取数据的函数getVisibleTodos
,这样返回的todolist
就不会受到一些没必要要的state的变化而变化引发冲渲染。
总结了那么多的用法,其实也是redux
的基本用法,而后本身写了半天的todolist
,把上面说到的技术都用了,这是 github地址,上面的内容若有错误,勿喷,毕竟入门级别。。。