Redux
JavasSript 的状态容器
跟 React 没有关系,Redux 支持 React、Angular、Ember、JQuery 甚至 JavaScript。
Action、Reducer、Store
Redux
三大原则
单一数据源
State 是可读的
使用纯函数来执行
Action
概念:
记录了用户行为的数据的载体
Action 是 Store 数据的唯一来源
定义:
Action 是一个 JavaScript 对象
Action 内有一个 Type 字段
Action 通常被定义为字符串常量
尽量减少在 Action 中传递的数据
设计 Todo 所需的 Action
var actionAddTodo = { type: 'ADD_TODO', text: '吃饭'};var actionCompleteTodo = { type:'COMPLETE_TODO', index:2};var actionSelectFilter = { type:'SETFILTER', filter:'SHOW_ALL'};Action 函数
// 创建对象的工厂模式function createAction(text){ var o = new Object();
o.type = ADD_TODO;
o.text = text; return o;
}function addTodo(text){ return { type: 'ADD_TODO',
text
}
}State
概要
存放程序数据的一颗 ,或者说是一个数据库。
State 是只读的,可能是一个 JavaScript 对象、数组、Immutable.js 的数据结构
唯一能更新 State 的方法就是触发 Action ,使用 Store 的 Dispatch 更新 State 。
为什么强调 State 只读
单一数据源,State 是程序的唯一数据源
确保视图和网络请求只能表现出我想要修改 State , 然后通过触发 Action 修改
只有唯一更新 State 方法,我们可以更容易的实现撤销 / 重做这类应用
设计 State
Todo 的任务列表
var initState = { filter: 'SHOW_ALL', todos: []
}设计 State 注意事项
应该尽量是 State 可以轻松的转化为 JSON
尽可能把 State 规范化,不存在嵌套
把所有数据都放到一个对象里,每个数据以 ID 作为主键
把 State 想象成一个数据库
//方式一[{ id: 1, title: 'Some Article', author: { id: 1, name: 'Dan'
}
}, { id: 2, title: 'Other Article', author: { id: 1, name: 'Dan'
}
}]//方式二{ result: [1, 2], entities: { articles: { 1: { id: 1, title: 'Some Article', author: 1
}, 2: { id: 2, title: 'Other Article', author: 1
}
}, users: { 1: { id: 1, name: 'Dan'
}
}
}
}Object.assign(target,...source) 函数
把所有的源对象属性复制到目标对象并放回。
// 用法一var o1 = {a:1};var o2 = {b:2};var o3 = {c:3};var obj1 = Object.assign(o1,o2,o3);console.log(o1); // {a:1,b:2,c:3}console.log(obj1); // {a:1,b:2,c:3}// 用法二var o4 = {a:1,b:2};var o5 = {b:3,c:4};var obj2 = Object.assign({},o4,o5);console.log(obj2); // {a:1,b:2,c:3,d:4}怎么使用 Object.assign()
必须保证 Reducer 是一个纯函数,我们不能改变传入的 State,所以我们需要使用 Object.assign({},state)复制一个 State
var state = {filter:'SHOW_ALL',todos:['x1','x2']};var obj3 = Object.assign({},state,{todos:[state.todos[1],'x3']})console.log(obj3)Reducer
var createStore = Redux.createStore;var combineReducers = Redux.combineReducers;var applyMiddleware = Redux.applyMiddleware;const ADD_TODO = 'ADD_TODO';const COMPLETE_TODO = 'COMPLETE_TODO';const SETFILTER = 'SETFILTER';const FILTER = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETE: 'SHOW_COMPLETE', SHOW_ACTIVE: 'SHOW_ACTIVE'}function addTodo(text){ return { type: ADD_TODO,
text
}
}function completeTodo(index){ return { type: COMPLETE_TODO,
index
}
}function selectFilter(filter){ return { type: SETFILTER,
filter
}
}var initState = { filter:'SHOW_ALL', todos: []
}function todoApp(state = initState,action){ switch(action.type){ case SETFILTER: return Object.assign({},state,{ filter: action.filter
}); case ADD_TODO: return Object.assign({},state,{ todos:[...state.todos,{text: action.text,complete: false}]
}); case COMPELETE_TODO: return Object.assign({},state,{ todos: return [
...state.slice(0, parseInt(action.index), Object.assign({},state[action.index],{ completed: true
}),
...state.slice(parseInt(action.index) + 1)
]
}); default: return state;
}
}拆分 Reducer
将不存在以来关系的字段拆分给不同的子 Reducer 管理。
例如 Filter 和 Todos 俩个字段不存在相互依赖。
function setFilter(state = FILTER.SHOW_ALL,action){ switch(action.type){ case SETFILTER: return action.filter; default: return state;
}
}function todos(state = [], action){ switch(action.type){ case ADD_TODO: return [...state, { text: action.type, completed: false
}]; case COMPLETE_TODO: return [
...state.slice(0,parse(action.index)), Object.assign({},state[action.index],{ completed: true
}),
...state.slice(parseInt(action.index) + 1)
]; default: return state;
}
}combineReducers 的使用
将每个 Reducer 拼接起来返回一个完整的 state
函数部分源码
// reducers -> Object// example -> reducers = { filter: setFilter, todos: todos}function combineReducers(reducers) { var reducerKeys = Object.keys(reducers) // array -> ['filter','todos']
var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] // When i = 0 The key = filter
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
} var finalReducerKeys = Object.keys(finalReducers) // ......
return function combination() { var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0] var action = arguments[1] var hasChanged = false
var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++ ) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) // ......
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
} return hasChanged ? nextState : state
}
}使用例子
var todoApp = combineReducers({ filter: setFilter, todos: todos
})Store
维持应用所有 State 的一个对象,也可是说一个方法的集合
var store = createStore(todoApp)
Store 的方法
getState
dispatch 唯一能改变 state 的函数
subscribe 增加监听,当 dispatch action 的时候就会触发
replaceReducer 替换当前用来计算的 reducer
var createStore = Redux.createStore;var combineReducers = Redux.combineReducers;var applyMiddleware = Redux.applyMiddleware;const ADD_TODO = 'ADD_TODO';const COMPLETE_TODO = 'COMPLETE_TODO';const SETFILTER = 'SETFILTER';const FILTER = { SHOW_ALL:'SHOW_ALL', SHOW_COMPLETE:'SHOW_COMPLETE', SHOW_ACTIVE:'SHOW_ACTIVE'}function addTodo(text){ return { type:ADD_TODO,
text
}
}function completeTodo(index){ return { type:COMPLETE_TODO,
index
}
}function selectFilter(filter){ return { type:SETFILTER,
filter
}
}var initState = { filter:'SHOW_ALL', todos:[]
}function todos(state = [], action) { switch (action.type) { case ADD_TODO: return [...state, { text: action.text, completed: false
}]; case COMPLETE_TODO: return [
...state.slice(0, parseInt(action.index)), Object.assign({}, state[action.index], { completed: true
}),
...state.slice(parseInt(action.index)+ 1)
]; default: return state;
}
}function setFilter(state = FILTER.SHOW_ALL,action){ switch(action.type){ case SETFILTER: return action.filter; default: return state;
}
}var todoApp = combineReducers({ filter:setFilter, todos:todos
});var store = createStore(todoApp);var unsubscribe = store.subscribe(()=>{ console.log(store.getState());
});console.log('添加吃饭');
store.dispatch(addTodo('吃饭'));console.log('添加睡觉');
store.dispatch(addTodo('睡觉'));console.log('完成吃饭');
store.dispatch(completeTodo(0));console.log('添加打豆豆');
store.dispatch(addTodo('打豆豆'));console.log('完成睡觉');
store.dispatch(completeTodo(0));console.log('setFilter');
store.dispatch(selectFilter(FILTER.SHOW_COMPLETE));
unsubscribe();小结
Action 用户发起一个动作请求的请求内容
State 保存应用现有的状态
Reducer 根据 Action 请求内容的 Type 字段去匹配要进行的动作与修改的状态
Store 存储库,把 Reducer 传入 createStore 的构造器中得到,只有通过它的 dispatch 方法传入一个 Action 请求内容,之后自动去 Reducer 中匹配 Type 字段,之后去修改相应的状态
作者:Nodelover
链接:https://www.jianshu.com/p/4417458ddab2