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