虾写前端技术文前端开发那些事

JavaScript 的状态容器 Redux

2016-06-03  本文已影响362人  Nodelover

Redux

JavasSript 的状态容器
跟 React 没有关系,Redux 支持 React、Angular、Ember、JQuery 甚至 JavaScript。

Action、Reducer、Store

Redux

三大原则

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 只读

设计 State

Todo 的任务列表

var initState = {
    filter: 'SHOW_ALL',
    todos: []
}

设计 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 的方法

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();

小结

上一篇 下一篇

猜你喜欢

热点阅读