Redux 入門總結

Redux 入門總結

介紹

Redux 是管理應用狀態的js庫。是單向數據流應用架構的一種比較優雅的實現。javascript

Redux 和 React 之間沒有關係。Redux 支持 React、Angular、Ember、jQuery 甚至純 JavaScript。Redux 無依賴。html

三大原則java

  • state 以單一對象存儲在 store 對象中。git

  • state是隻讀的:這裏和Flux思想一致。惟一改變 state 的方法是觸發事先定義好的 action。es6

  • 使用純函數 reducer 執行 state 更新。github

三大概念redux

  • Action數據結構

  • Reducer架構

  • Storeapp

Action : 本質上是 JavaScript 普通對象

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store. You send them to the store using store.dispatch().

Action 一般只是描述某一特定事情。例若有一個加入購物車的 action:

{
  type: 'ADD_CART',
  goods: {
    ...
  }
}

一般狀況下,咱們會使用建立函數(Action Creater)返回 Action 對象。

const ADD_CART = 'ADD_CART';

function addToCart(goods) {
  return {
    type: ADD_CART,
    goods
  };
}

惟一改變 state 的方法是觸發事先定義好的 action。

state = store.getState();
store.dispatch(addToCart(goods));
state = store.getState();

Reducer

概念

Actions describe the fact that something happened, but don’t specify how the application’s state changes in response. This is the job of a reducer.

Reducer 是什麼呢,Reducer 是用來處理數據邏輯的,具體就是根據 Action 處理 State 的函數。

(previousState, action) => newState

Action 並無指出應用要如何更新購物車對應的 State。怎麼更新 State 就是 reducer 要作的事情了。

編寫 Reducer

編寫 reducer 前須要肯定頁面或應用的 state,state 結構肯定後 reducer 就出來了。

這裏有兩個問題:

  • 一、怎樣設計 state 的數據結構?

  • 二、有了 state 結構,怎麼編寫 reducer?

設計 State 結構

在 Redux 應用中,全部的 state 都被保存在一個單一對象中(這點跟官方的Flux實現不同)。以最簡的形式把應用的 state 用對象描述出來(這點跟官方的Flux實現同樣)。

一般,這個 state 樹還須要存放其它一些數據,例如 UI 相關的 state。這樣作沒問題,但儘可能把這些數據與 UI 相關的 state 分開。

例如 state 結構以下:

{
  goods:{},
  carts: [{
    ...
  }, {
    ...
  }],
  comments: [{
    ...
  }, {
    ...
  }]
  //相關的 UI state
}

根據 State 結構編寫 reducer

對於上面的結構編寫對應的 reducer 就相似:

function app(state = initialState, action) {//這裏一個技巧是使用 ES6 參數默認值語法 來精簡代碼。
  switch (action.type) {
    case 'ACTION_NAME1':
      //處理state //注意1:返回 新的狀態,不要修改 state。 應該新建了一個副本。
      return newState;
    case 'ACTION_NAME1':
      //處理state 返回 新的狀態
      return newState;
    default://注意2:在 default 狀況下返回舊的 state, 若是沒有舊的狀態就返回初始狀態 initialState
      return state;
  }
}

而 reducer 是能夠拆分和合成的。真正開發項目的時候 State 會涉及不少功能,在一個 reducer 中處理全部邏輯會很是混亂,因此須要拆分紅多個小 reducer,
每一個 reducer 只處理它管理的那部分State數據。因此根據 state 的結構調整拆分上面的 reducer :

function goods(state = Map(), action) {
  switch (action.type) {
    case types.INIT_GOODS:
      state = Immutable.fromJS(action.goods);
      return state;
    case types.SET_GOODS_COLLECT:
      state = state.update('isCollect', v=>!v);
      state = state.update('collectNum', v=> (state.get('isCollect') ? v + 1 : v - 1));
      return state;
    default:
      return state;
  }
}

function comments(state = List(), action) {
  switch (action.type) {
    case types.INIT_COMMENTS:
      state = Immutable.fromJS(action.comments);
      return state;
    case types.GET_MORE_COMMENT:
      action.comments.map(function(item) {
        state = state.push(item);
      });
      return state;
    default:
      return state;
  }
}

function carts(state = List(), action) {
  switch (action.type) {
    case types.INIT_CARTS:
      state = Immutable.fromJS(action.carts);
      return state;
    case types.ADD_CART:
      state = Immutable.fromJS(action.carts);
      return state;
    default:
      return state;
  }
}

redux 提供 combineReducers() 函數來調用咱們編寫的一系列 reducer,每一個 reducer 根據它們的 key 來篩選出 state 中的一部分數據並處理,而後這個生成的函數所全部 reducer 的結果合併成一個大的對象。

const rootReducer = combineReducers({
  goods,
  comments,
  carts
});

export default rootReducer;

至此根據 state 結構編寫 reducer 完成。

Store

action 用來描述「發生了什麼」, reducers 會根據 action 更新 state。

Store 就是把它們聯繫到一塊兒的對象,是用來維持應用的 state 的。

store有四個方法。

  • getState: 獲取應用當前 state。

  • subscribe:添加監聽器。

  • dispatch:分發 action。更新 state。

  • replaceReducer:替換 store 當前用來處理 state 的 reducer。

經常使用的是dispatch,這是修改State的惟一途徑,使用起來也很是簡單。

Redux 應用只有一個單一的 store。當須要拆分處理數據的邏輯時,使用 reducer 組合 而不是建立多個 store。而官方的 Flux 實現,則是一個應用能夠有一個或多個 store, Flux 中 store 的劃分可大可小。

建立一個 store :

let store = createStore(rootReducers, initialState);

獲取 state :

store.getState();

更新 state :

store.dispatch(addToCart(goods));

實例

官方例子

example.js http://rackt.org/redux/

import { createStore } from 'redux'

/**
 * This is a reducer, a pure function with (state, action) => state signature.
 * It describes how an action transforms the state into the next state.
 *
 * The shape of the state is up to you: it can be a primitive, an array, an object,
 * or even an Immutable.js data structure. The only important part is that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * In this example, we use a `switch` statement and strings, but you can use a helper that
 * follows a different convention (such as function maps) if it makes sense for your project.
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counter)

// You can subscribe to the updates manually, or use bindings to your view layer.
store.subscribe(() =>
  console.log(store.getState())
)

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

完整一點的例子

example2.js

import { combineReducers, createStore } from 'redux'
var objectAssign = require('object-assign');

// Action
const ADD_CART = 'ADD_CART';
const SET_GOODS_COLLECT = 'SET_GOODS_COLLECT';

function addToCart(goods) {
  return {
    type: ADD_CART,
    goods
  };
}

function collectGoods() {
  return {
    type: SET_GOODS_COLLECT
  };
}

// reducers
var initGoods = {"id": 123, "name": "星戰正版機器人BB-8", isCollect: false};
function goods(state = initGoods, action) {
  switch (action.type) {
    case SET_GOODS_COLLECT:
      return objectAssign({}, state, {isCollect: !state.isCollect});

    default:
      return state;
  }
}

var initCarts = [];
function carts(state = initCarts, action) {//es6 特性
  switch (action.type) {
    case ADD_CART:
      return [...state, action.goods];//es6 特性
    default:
      return state;
  }
}

const rootReducer = combineReducers({
  goods,
  carts
});

let store = createStore(rootReducer);

console.log(store.getState());
store.dispatch(collectGoods());
console.log(store.getState());

store.dispatch(addToCart({"id": 124, "name": "星戰正版機器人BB-8", isCollect: false}));
console.log(store.getState());
store.dispatch(addToCart({"id": 125, "name": "星戰正版機器人BB-10", isCollect: false}));
console.log(store.getState());

在這裏只是簡單介紹了 Redux,至於 Redux 怎麼結合 React 使用,怎麼處理異步請求等,將在以後再進行總結。

接觸Redux會涉及到一些感念,好比純函數、組合函數、函數柯里化。能夠參考文章Functional Programming for JavaScript People
參考:

相關文章
相關標籤/搜索