Redux入門學習系列教程(一)

Redux入門學習系列教程(一)
Redux入門學習系列教程(二)
Redux入門學習系列教程(三)
Redux入門學習系列教程(四)

作者結(jié)合文檔,給出入門Redux學習 Demo示例

https://github.com/guangqiang-liu/react-native-reduxDemo

本教程主要講解Redux的核心 API 以及 工作流程

什么是Redux

Redux 是 JavaScript 狀態(tài)容器,提供可預測化的狀態(tài)管理。可以讓你構建一致化的應用,運行于不同的環(huán)境(客戶端、服務器、原生應用),并且易于測試。不僅于此,它還提供 超爽的開發(fā)體驗,比如有一個時間旅行調(diào)試器可以編輯后實時預覽

安裝Redux

  • npm install redux --save

多數(shù)情況下,我們還需要使用 React 綁定庫和開發(fā)者工具。

  • npm install redux-devtools --save-dev

核心API

  • Action
  • Reducer
  • Store

Redux三大原則

  • 單一數(shù)據(jù)源

整個應用的 state 被儲存在一棵 object tree 中,并且這個 object tree 只存在于一個 唯一的store 中。

  • State 只讀

惟一改變 state 的方法就是觸發(fā) action,action 是一個用于描述已發(fā)生事件的普通對象。

  • 使用純函數(shù)來執(zhí)行修改

為了描述 action 如何改變 state tree ,你需要編寫 reducers。

Action

  • Action官方解釋:把數(shù)據(jù)從應用傳到 store 的有效載荷。它是 store 數(shù)據(jù)的唯一來源。一般來說你會通過 store.dispatch() 將 action 傳到 store。
  • 其實Action就是一個普通的對象,其中的type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設置,參照 Flux 標準 Action 獲取關于如何構造 action 的建議。

redux約定 Action 內(nèi)使用一個字符串類型的type字段來表示將要執(zhí)行的動作名稱。

{
    type: 'ADD_TODO'
}

除了type 之外,Action還可以攜帶需要的數(shù)據(jù)。

{
    type: 'ADD_ITEM',
    text: 'Learn Redux'
}

Action Creator

View 要發(fā)送多少種消息,就會有多少種 Action。如果都手寫,會很麻煩??梢远x一個函數(shù)來生成 Action,這個函數(shù)就叫 Action Creator。

const ADD_TODO = '添加 TODO'

function addTodo(text) {
  return {
    type: ADD_TODO,
    text:text
  }
}

const action = addTodo('Learn Redux')

上面代碼中,addTodo函數(shù)就是一個 Action Creator,在 Redux 中的 Action Creator 只是簡單的返回一個 action而已

傳統(tǒng)的 Flux 實現(xiàn)中,當調(diào)用 Action Creator時,一般會觸發(fā)一個 dispatch

function addTodoWithDispatch(text) {
  const action = {
    type: ADD_TODO,
    text:text
  }
  dispatch(action)
}

在Redux 中只需把 Action Creator的結(jié)果傳給 dispatch() 方法即可發(fā)起一次 dispatch 過程。

dispatch(addTodo(text))

或者創(chuàng)建一個被綁定的 Action Creator 來自動觸發(fā)dispatch

const bindAddTodo = (text) => dispatch(addTodo(text))

然后直接調(diào)用這個函數(shù)即可完成一次dispatch過程

bindAddTodo(text)

注意:
store 里能直接通過 store.dispatch() 調(diào)用dispatch()方法,但是多數(shù)情況下我們選擇使用 react-redux 提供的 connect() 幫助器來調(diào)用。bindActionCreators() 可以自動把多個 action 創(chuàng)建函數(shù) 綁定到 dispatch() 方法上

Reducer

  • Store 收到 Action 以后,必須給出一個新的 State,這樣 View 才會發(fā)生變化。這種 State 的計算過程就叫做 Reducer。

  • Reducer 就是一個普通的函數(shù)

  • 當被Redux調(diào)用的時候會給Reducer傳遞兩個參數(shù):State 和 Action

  • 它會根據(jù) Action 的type屬性來對舊的 State 進行操作,返回新的State

const defaultState = 10

const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case Constants.INCREASE:
      return state + 1
    case Constants.DECREASE:
      return state - 1
    default:
      return state
  }
}

const state = reducer(10, {
  type: Constants.INCREASE
})

上面代碼中,reducer函數(shù)收到名為INCREASE的 Action 后,就返回一個新的 State,作為加法的計算結(jié)果。

實際開發(fā)中,Reducer 函數(shù)不用像上面這樣手動去調(diào)用,store.dispatch方法會觸發(fā) Reducer 的自動調(diào)用,為此,Store 需要知道 Reducer 函數(shù),做法就是在生成 Store 的時候,將 Reducer 傳入到createStore函數(shù)中。

import { createStore } from 'redux'
import reducer from '../reducer'

// 創(chuàng)建store
const store = createStore(reducer)

上面代碼中,createStore接受 Reducer 作為參數(shù),生成一個新的 Store。這樣以后每當store.dispatch發(fā)送過來一個新的 Action,就會自動調(diào)用 Reducer,得到新的 State

Reducer 的拆分

真正開發(fā)項目的時候State會涉及很多功能,在一個Reducer函數(shù)中處理所有邏輯會非?;靵y,所以需要拆分成多個子Reducer,每個子Reducer只處理它管理的那部分State數(shù)據(jù)。然后在由一個主rootReducers來專門管理這些子Reducer。

Redux提供了一個方法:combineReducers專門來管理這些子Reducer

import {createStore, combineReducers} from 'redux'

const list = (state = [], action) => {
  switch (action.type) {
    case ADD_ITEM:
      return [createItem(action.text), ...state]
    default:
      return state
  }
}

const counter = (state = defaultState, action) => {
  switch (action.type) {
    case Constants.INCREASE:
      return state + 1
    case Constants.DECREASE:
      return state - 1
    default:
      return state
  }
}

let rootReducers = combineReducers({list, counter})

combineReducers 生成了一個類似于Reducer的函數(shù)。為什么是類似于尼,因為它不是真正的Reducer,它只是一個調(diào)用Reducer的函數(shù),只不過它接收的參數(shù)與真正的Reducer一模一樣

combineReducers 核心源碼解讀

function combineReducers(reducers) {

  // 過濾reducers,把非function類型的過濾掉~
  var finalReducers = pick(reducers, (val) => typeof val === 'function');

  var defaultState = mapValues(finalReducers, () => undefined);

  return function combination(state = defaultState, action) {
    // finalReducers 是 reducers
    var finalState = mapValues(finalReducers, (reducer, key) => {

      // state[key] 是當前Reducer所對應的State,可以理解為當前的State
      var previousStateForKey = state[key];
      var nextStateForKey = reducer(previousStateForKey, action);

      return nextStateForKey;      
    });

    // finalState 是 Reducer的key和stat的組合。。
  }
}

從上面的源碼可以看出,combineReducers生成一個類似于Reducer的函數(shù)combination。

注意:
當使用combination的時候,combination會把所有子Reducer都執(zhí)行一遍,子Reducer通過action.type 匹配操作,因為是執(zhí)行所有子Reducer,所以如果兩個子Reducer匹配的action.type是一樣的,那么都會匹配成功。

Store

  • Store 就是保存數(shù)據(jù)的地方,你可以把它看成一個容器。整個應用只能有一個 Store。
  • Redux 提供createStore這個函數(shù),用來生成 Store。
  • 再次強調(diào)一下: Redux 應用只有一個單一的 store。當需要拆分數(shù)據(jù)處理邏輯時,你應該使用 reducer 組合 而不是創(chuàng)建多個 store。
  • 根據(jù)已有的 reducer 來創(chuàng)建 store 是非常容易的。在前一個章節(jié)中,我們使用 combineReducers() 將多個 reducer 合并成為一個?,F(xiàn)在我們將其導入,并傳遞給createStore函數(shù)。
import { createStore } from 'redux'
import reducer from '../reducer'

const store = createStore(reducer)

Store提供暴露出四個API方法

  • store.getState(): 獲取應用當前State。
  • store.subscribe():添加一個變化監(jiān)聽器。
  • store.dispatch():分發(fā) action。修改State。
  • store.replaceReducer():替換 store 當前用來處理 state 的 reducer。
import { createStore } from 'redux'
let { subscribe, dispatch, getState, replaceReducer} = createStore(reducer)

下面是createStore方法的一個簡單實現(xiàn),可以了解一下 Store 是怎么生成的。

const createStore = (reducer) => {
  let state;
  let listeners = [];

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };

  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener);
    }
  };

  dispatch({});

  return { getState, dispatch, subscribe };
}

發(fā)起 Actions

現(xiàn)在我們已經(jīng)創(chuàng)建好了 store ,讓我們來驗證一下!雖然還沒有界面,我們已經(jīng)可以測試數(shù)據(jù)處理邏輯了。

import { addTodo, toggleTodo, setVisibilityFilter, VisibilityFilters } from './actions'

// 打印初始狀態(tài)
console.log(store.getState())

// 每次 state 更新時,打印日志
// 注意 subscribe() 返回一個函數(shù)用來注銷監(jiān)聽器
let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
)

// 發(fā)起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// 停止監(jiān)聽 state 更新
unsubscribe()

可以看到 store 里的 state 是如何變化的:

img

可以看到,在還沒有開發(fā)界面的時候,我們就可以定義程序的行為。而且這時候已經(jīng)可以寫 reducer 和 action 創(chuàng)建函數(shù)的測試。不需要模擬任何東西,因為它們都是純函數(shù)。只需調(diào)用一下,對返回值做斷言,寫測試就是這么簡單。

Redux 工作流

flow
  • 首先,用戶發(fā)出 Action
store.dispatch(action)
  • 然后,Store 自動調(diào)用 Reducer,并且傳入兩個參數(shù):當前 State 和收到的 Action。 Reducer 會返回新的 State
let nextState = todoApp(previousState, action)
  • State 一旦有變化,Store 就會調(diào)用監(jiān)聽函數(shù)
// 設置監(jiān)聽函數(shù)
store.subscribe(listener)
  • listener可以通過store.getState()得到當前狀態(tài)。如果使用的是 React,這時可以觸發(fā)重新渲染 View
function listerner() {
  let newState = store.getState();
  component.setState(newState);   
}

參考文獻

阮一峰講解redux
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html

http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

Redux中文文檔

Redux中文文檔

Redux其他系列教程

https://github.com/lewis617/react-redux-tutorial

https://github.com/kenberkeley/redux-simple-tutorial

官方todoApp示例分析

Redux入門學習系列教程(一)
Redux入門學習系列教程(二)
Redux入門學習系列教程(三)
Redux入門學習系列教程(四)

總結(jié)

本系列教程是參照阮老師的Redux入門教程文章和Redux中文文檔進行的整合和拓展。更多更詳細的Redux使用方式請參照上面的參考文獻。

福利時間

  • 作者React Native開源項目OneM地址(按照企業(yè)開發(fā)標準搭建框架設計開發(fā)):https://github.com/guangqiang-liu/OneM (歡迎小伙伴們 star)
  • 作者簡書主頁:包含50多篇RN開發(fā)相關的技術文章http://m.itdecent.cn/u/023338566ca5 (歡迎小伙伴們:多多關注,多多點贊)
  • 作者React Native QQ技術交流群:620792950 歡迎小伙伴進群交流學習
  • 友情提示:在開發(fā)中有遇到RN相關的技術問題,歡迎小伙伴加入交流群(620792950),在群里提問、互相交流學習。交流群也定期更新最新的RN學習資料給大家,謝謝支持!
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容