为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript 的状态容器 Redux

标签:
JavaScript

Redux

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

Action、Reducer、Store

600

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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消