1. Redux 基础
Redux
三大原则
-
单一数据源;(
redux
只有一个数据源,) -
State 只读; (
State
不能被直接修改,只能dispatch
一个action
的方式来返回一个新的状态); -
只能通过纯函数(
reducer
)去修改store
;(reducer
描述了如何将action
里的信息和原state
组合成一个新的状态)
如何使用
见 Redux 中文文档 ,
Redux
文档已经详细的描述了Redux
的基础使用、进阶和一些技巧, 务必详细阅读 。
简单来说,首先我们需要创建一个
store
,用于存放
State
。
const configStore => () {
const store = createStore(reducers, initState, middlewares)
reutrn store
}
复制代码
然后创建各个
reducer
,并使用
combineReducer
来合成它们。
const count = (state={}, acrtion) => {...}
const name = (state={}, acrtion) => {...}
export default combineReducer({
count,
name
})
复制代码
然后创建需要的
action
,通常我们使用
action creater
来创建
action
。
action
描述发生了什么,它承载了当前操作的操作类型和数据
。
const addCount = (count) => ({
type: "ADD", // 这个表示当前action的操作类型,在reducer中需要依据action type 来执行相应的操作;
count // action 的操作数据
})
复制代码
然后只要使用
dispatch(addCount(xxx))
,就能修改
store
里的
count state
了。
但是通常情况下,我们会借助第三方库
react-redux
来帮忙封装,所以不需要直接手动在组件中操作
dispatch
,库
react-redux
,将这个操作移到了
mapDispatchToProps
中了。
本文将总结阅读源码后的感触。
2.
Reducer
和
combineReducers
在
Redux
中,我们可以使用
reducer
来创建新的
state
。
第一次创建
state
是什么时候?
我们知道即使我们没有给
createStore
传递
initState
,
Redux
也会生成一个初始
state
树,如下面的例子,
redux
会返回
{ count: 0, name: { first: 'huanqiang', last: 'wang' } }
,那么这是如何实现的呢?
const countReducer = (state = 0, action) => {
switch (action.type) {
case 'ADD':
return state + 1
case 'DIV':
return state - 1
default:
return state
}
}
const firstNameReducer = (state = 'wang', action) => {
switch (action.type) {
case 'FIRST_LONG_CHANGE':
return 'huanqiang'
case 'FIRST_SHORT_CHANGE':
return 'hq'
default:
return state
}
}
const lastNameReducer = (state = 'huanqiang', action) => {
switch (action.type) {
case 'LAST_LONG_CHANGE':
return 'wang'
case 'LAST_SHORT_CHANGE':
return 'w'
default:
return state
}
}
const nameReducer = combineReducer({
first: firstNameReducer,
last: lastNameReducer
})
const reducers = combineReducer({
count: countReducer,
name: nameReducer
})
复制代码
如果你有心的话,当你打印了所有的
action
的时候,你在应用启动后会在
chrome console
窗口看到
@@redux/INITxxxxxxx
字样的信息,这个信息就表示
Redux
进行了
State
的初始化,
Redux
在你执行
createStore
的时候,在
return
之前进行了一次
dispatch
,而
action
正是
{ type: ActionTypes.INIT }
。
reducers
是什么样子的?
其实这个问题和
combineReducer
会得到一个怎样结构的返回值是一样的。
我们把上面的例子中的
reducers
简化一下其实就是如下函数,接受最顶层的
state
,然后返回一棵
state
的树。同时
Redux
处理每一个子
reducer
的时候,都会从树中获取属于该
reducer
的
state
,然后再作为参数传递进去,比如
countReducer
,
Redux
就是先获取属于
count
的状态
state['count']
,然后和
action
一起作为参数传入
countReducer
,并将其返回值作为
count
的
value
。
function(state={}, action) {
return {
count: countReducer(state['count'], action),
name: (state1={}, action) => {
first: firstNameReducer(state1['first'], action),
last: lastNameReducer(state1['last'], action)
}(state['name'], action)
}
}
复制代码
这里我们放一个简化的
combineReducers
的源码:
这个是我自己实现的,和官网的略有出入。
export default reducers => {
return (state = {}, action) => {
// 初始化新的state
const nextState = {}
const reducerKeys = Object.keys(reducers)
let hasChanged = false
for (const key of reducerKeys) {
// key 就是我们传入 combineReducers 函数的 object 的 key 值,也就是 state 的 key。比如 上文的 count。
// reducer 就是我们传入 combineReducers 函数的 object 的 value。
const reducer = reducers[key]
// 获取之前的 state
const prevState = state[key]
// 执行 reducer 函数
nextState[key] = reducer(prevState, action)
if (!hasChanged) {
hasChanged = prevState !== nextState[key]
}
}
return hasChanged ? nextState : state
}
}
复制代码
看吧,
combineReducers
函数是如此简单,以至于都没什么好写的,至于
dispatch
函数,其实更加简单,去掉边界条件判断和监听操作之后,核心
change state
的代码就这么一行。
currentState = currentReducer(currentState, action)
复制代码
然后是
getState
,去掉边界条件判断也超级简单:
function getState() {
return currentState
}
复制代码
3.
applyMiddleware
首先,先上一个中间件的示例:
来自于 Redux 中文文档 - Middleware 一文 最下方的例子
/**
* 记录所有被发起的 action 以及产生的新的 state。
*/
const logger = store => next => action => {
console.group(action.type)
console.info('dispatching', action)
let result = next(action)
console.log('next state'