Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,并且actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。我们需要根据action返回的数据来判断,返回新的state。
reducer是一个纯函数,接收旧的 state 和 action,返回新的state。如:
// reducer
export function user(state=initState, action){
switch(action.type){
case AUTH_SUCCESS:
return {...state, msg:'',redirectTo:getRedirectPath(action.payload),...action.payload}
case LOAD_DATA:
return {...state, ...action.payload}
case ERROR_MSG:
return {...state, isAuth:false, msg:action.msg}
case LOGOUT:
return {...initState,redirectTo:'/login'}
default:
return state
}
}
之所以将这样的函数称之为reducer,是因为这种函数与被传入 Array.prototype.reduce(reducer, ?initialValue) 里的回调函数属于相同的类型。保持 reducer 纯净非常重要。永远不要在 reducer 里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如 API 请求和路由跳转;
- 调用非纯函数,如 Date.now() 或 Math.random()。
只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
注意:不要直接修改state,可以开启对ES7提案对象展开运算符的支持, 从而使用 { ...state, ...newState } 达到相同的目的。 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。
以上是使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器。
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,应该使用 reducer 组合 而不是创建多个 store。
根据已有的 reducer 来创建 store 是非常容易的。现在我们将 reducer 导入,并传递 createStore()。
import { createStore } from 'redux'
import todoApp from './reducers' #导入reducer
let store = createStore(todoApp)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。如:
const store = createStore(reducers, compose(
applyMiddleware(thunk),
window.devToolsExtension?window.devToolsExtension():f=>f
))
let store = createStore(todoApp, window.STATE_FROM_SERVER)
发起 Actions
现在我们已经创建好了 store ,让我们来验证一下!虽然还没有界面,我们已经可以测试数据处理逻辑了
import { createStore } from 'redux'
import todoApp from './reducers'
import {
addTodo,
toggleTodo,
setVisibilityFilter,
VisibilityFilters
} from './actions'
let store = createStore(todoApp)
// 打印初始状态
console.log(store.getState())
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))
// 停止监听 state 更新
unsubscribe();