Redux
state
一个简单的对象,用于保存数据。
Action
Action
是把数据从应用传到store
的有效载荷。
说的简单点,就是一个具有 type
属性的对象
{
type: 'ADD_TODO',
text: 'hello world'
}
对象的 type
属性规定了 Action
的行为: ADD_TODO
,其他属性作为该 Action
需要处理的数据。
可以手动编写,也可以由函数生成。
function createTodo(text){
return {
type: 'ADD_TODO',
text: text
}
}
函数生成可以简化 Action
的编写,同时将 type
固化,不用特意去找 Action
的类型。
Reducer
Reducers
指定了应用状态的变化如何响应 actions
并发送到 store
的过程,记住 actions
只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
。
Reducer
是一个函数,接收 state
和 action
作为参数.
函数内部将 action
应用于 state
生成一个新的 state
。
函数行为可以描述为:
(previousState, action) => newState
注: 一个 Reducer
需要有一个明确的返回,因此它应该是一个同步纯函数。
由于在 redux
初始化时,状态树为空,因此 state
需要有一个默认值。使用 ES6
默认值的写法处理。
function todoApp(state = [], action){
switch (action.type) {
case 'ADD_TODO':
return [
...state,
action.text
]
default:
return state
}
}
注:
- 不要修改原本的
state
这会照成副作用,也不符合纯函数的定义。 - 在遇到未知情况时,如
action
的type
出错,那么一定要返回旧的state
。
一个 Reducer
可以对应整个状态树的一部分(当然也可以是一整课树),多个不同的 Reducer
可以通过组合形成一个针对于完整状态树的处理函数。
function todos(state = [], action){
switch (action.type) {
case 'ADD_TODO':
return [
...state,
action.text
]
default:
return state
}
}
function other(state = {}, action){
switch (action.type) {
// ...
default:
return state
}
}
function appReducers(state = {}, action){
return {
todos: todos(state.todos, action),
other: other(state.other, action)
}
}
上诉代码产生了 3
个 Reducer
,两个部分的 Reducer
(todos & other
),一个由两个部分 Reducer
组合形成的完整的 Reducer
(App
)。
Redux
提供了 combineReducers
来处理 Reducer
的合并。作用就是上面的合并代码。
import { combineReducers } from 'redux'
// 设置的 key 与函数同名
const appReducers = combineReducers({
todes,
other
})
// 自定义设置 key
const appReducers2 = combineReducers({
a: todes,
b: other
})
当然我们也可以自己实现一下这个 combineReducers
。
function combineReducers(reducerObj){
return function(state = {}, action){
function createState(reducerObj, state){
let newState = {...state};
Object.entries(reducerObj).forEach(([name, value]) => {
// 如果是对象继续遍历,将返回值赋给 state
if(typeof value === 'object'){
newState[name] = createState(value, newState[name]);
// 如果是函数就执行将返回值赋给 state
}else if(typeof value === 'function'){
newState[name] = value(newState[name], action);
// 直接赋值
}else{
newState[name] = value;
}
})
return newState;
}
return createState(reducerObj, state)
}
}
结合 Action
我们来模拟下生成 state
和修改 state
的过程。
// 生成 state
function createState(reducer){
// 调用 reducer 生成 state
let init = reducer(undefined, {})
return init
}
const state = createState(appReducers)
console.log(state)
// { todos: [], other: {} }
function changeState(reducer, state, action){
// 调用 reducer 重新生成 state
let change = reducer(state, action)
return change
}
const state_2 = changeState(appReducers, state, {
type: 'ADD_TODO',
text: 'hello world'
})
console.log(state_2)
// { todos: [ 'hello world' ], other: {} }
通过 action/reducer
和两个执行函数(createState/changeState
)做到以下几点
- 生成一个状态树
state
- 将
action
对象表示的操作应用到state
- 生成一个新的状态树
state_2
这样就完成了不操作状态树的情况下通过 action
更改了状态树。
至于为什么要通过这么复杂的操作来实现对 state
的操作,有以下几点
- 增加操作
state
的难度,使得state
在正常操作中不会被轻易修改,有利于应用的稳定。 - 可以在
changeState
函数内截获对state
操作,实现发布/订阅模式,这样就可以通知订阅的函数执行,实现对绑定数据的更新。
createState
简单的实现一下
function createStore(reducer){
let state = reducer({});
let callbacks = [];
return {
getState(){
return state;
},
dispatch(action){
state = reducer(state, action);
callbacks.forEach(callback => callback(state));
},
subscribe(callback){
callbacks.push(callback);
}
}
}