JavaScript 的状态容器 Redux

作者: Nodelover | 来源:发表于2016-06-03 21:41 被阅读362次

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 字段,之后去修改相应的状态

相关文章

  • React实战之Redux

    Redux、React-Redux 简介 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。...

  • Redux

    Redux状态管理 Redux 是 JavaScript 状态管理容器,提供可预测的状态管理。redux 可以让你...

  • redux

    Redux Redux 是JavaScript状态的容器,提供可预测化的状态管理 Redux不是React必须的,...

  • SwiftUI 中使用 Redux 设计模式

    什么是Redux ?Redux 是 JavaScript 状态容器,提供可预测化的状态管理 Redux的工作原理 ...

  • redux 浅析

    什么是 Redux Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 Redux 是如何工...

  • Redux 分析

    Redux 是什么 Redux 是 Javascript 状态容器,提供可预测化的状态管理 Redux的功能及作用...

  • Redux详解

    Redux是什么 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 什么场景使用Redux...

  • redux 总结

    redux redux概念 Redux是JavaScript应用程序的可预测状态容器。 redux 设计思想 We...

  • redux入门

    什么是redux Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 什么时候使用redux...

  • 05-使用Redux进行状态管理

    什么是Redux Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux将应用程序所有...

网友评论

    本文标题:JavaScript 的状态容器 Redux

    本文链接:https://www.haomeiwen.com/subject/prlldttx.html