萌新 redux 入門

我不萌,但新。
2018年要結束了,這一年,我。。。算了,很少說了。
想起上半年看的 react,到如今也沒怎麼用過,都快忘光了,趁如今有時間我來捋捋,首先從 redux 下手(與 react 有啥什麼關係???😂)vue

爲何用 redux

我想學習 redux 的同窗多少知道它的做用,官方定義 redux 是一個面向 JavaScript 應用的可預測狀態容器。若是用過 vuex 的,就能明白它是幹嗎的,它們做用是同樣的,主要用來管理共享狀態。不一樣於 vuex 與 vue 的關係,redux 與 react 是解耦的,雖然 redux 一般用在 react 中。至於爲啥要用 redux,我以爲一張圖更好說明問題:react

在react應用中,父子組件之間的數據傳遞比較容易,可是在複雜組件層級關係中,如圖左所示的那樣,數據的傳遞就顯得很麻煩了,並且容易混亂,這時若是有一個容器幫咱們統一管理數據,並能共享到每一個組件,那就很方便咱們開發了,如圖右所示。而redux正是一個提供這樣功能的數據框架。vuex

基本概念

概念這東西我很差說,我仍是拿一張圖說明:npm

在 react 中使用 redux 時,某組件須要一個數據,那麼它就須要觸發一個 action,經過 dispatch 給 store,代表本身須要數據啦,action 是行爲動做,它告訴 store 須要什麼樣的數據,它也是 store 數據的惟一來源。store 至關於一個管理員,它本身不直接處理數據,而是把當前的數據和收到的 action 告訴 reducer,讓 reducer 來處理數據,並將新數據返還給 store,再由 store 傳遞給組件,組件拿到的就是目標數據了。redux

初看時,可能有點繞,咱們能夠理解爲一個在圖書館借書的場景。組件( Component)就至關於借書人,他要告訴管理員(Store),說:"我要借《時間簡史》",那麼說的這句話就能夠理解爲 Action,管理員(Store)聽到後,他不可能記得每本書的位置,因而就查看記錄本看看書的信息,那麼這個記錄本就至關於 Reducer,找到書的信息後,管理員(Store)就把信息告訴了借書人(Component)。可能比喻並非那麼恰當,但表達的意思也差很少了。redux 就是圍繞 Action,Store,Reducer 這三點來展開的。瀏覽器

基本使用

爲了方便起見,我直接用 create-react-app 腳手架建立一個項目來演示代碼,安裝以及建立我就略過了。把項目中多餘的文件刪除,src 目錄下只保留一個 index.js 文件。bash

以前說過,redux 和 react 是解耦的,那麼咱們先只在 index.js 文件中來使用一下 redux,使用前別忘記安裝如下 reduxapp

npm install redux
複製代碼

index.js框架

// 引入 createStore 方法,用於建立 store
import { createStore } from "redux"

// 默認初始狀態
const defaultState = {
  a: 1
}

// 建立 reducer
function reducer(state = defaultState, action) {
  switch (action.type) {
    case "ADD":
      return Object.assign({}, state, {
        b: action.num
      });
    default:
      return state;
  }
}

// 建立 store
const store = createStore(reducer)

// 獲取 state
console.log('dispatch action 以前的數據:',store.getState())

// 派發 action
store.dispatch({
    type:"ADD",
    num:2
})

// 獲取更新後的 state
console.log('dispatch action 以後的數據:',store.getState())

複製代碼

運行結果如圖:dom

以上就是一個redux的最基本的使用過程,咱們來看下。

reducer

首先說一下 reducer,它用來處理數據,本質上是一個函數,有兩個參數 state 和 action,state 便是保存的狀態,若是不給他賦初始值,那麼它就等於undefined,我在這裏給了它一個初始值 defaultState。action 便是行爲,它是一個對象,必須有一個名爲 type 的字段來表示將要執行的動做,如代碼裏的ADD就是我傳入的一個表示添加的行爲,(注意type的值可任意,應儘可能語意化)。除了 type 字段外,你能夠任意添加本身須要的字段,好比這裏我傳裏一個 num 字段,我須要將它添加到 state 中去。

reducer的注意事項:

  1. 不要直接修改 state。能夠看到,我再代碼裏使用 Object.assign() 新建了一個副本,再返回的。若不建立副本,redux 的全部操做都將指向內存中的同一個 state,全部的 state 都將被最後一次操做的結果所取代,咱們將沒法追溯 state 變動的歷史記錄。
  2. 在 default 狀況下返回舊的 state。即沒有 action 時,必定要返回舊的 state。

action

前面說了 action 本質上是一個JavaScript中的對象,約定 action 內必須使用一個字符串類型的 type 字段來表示將要執行的動做。多數狀況下,type 會被定義成字符串常量。當應用規模愈來愈大時,建議使用單獨的模塊或文件來存放 action,以下:

// actionTypes.js
export const ADD = "ADD"
複製代碼
import { ADD } from './actionTypes'

const action = {
    type: ADD,
    num: 2
}
複製代碼

經過store.dispatch()方法將 action 傳到 store。

store

store將 action 和 reducer 聯繫起來,維持應用的 state,能夠經過store.getState()來獲取 state,經過 store.dispatch() 來更新 state。咱們經過引入 createStore 方法,並傳入 reducer 爲第一個參數來建立 store。

在 react 中使用 redux

前面介紹了 redux 的基本使用方法,可是咱們最終仍是要使用在 react 中,如今咱們就結合 react 來用下 redux。

首先,修改項目目錄,添加store目錄,用來存放 redux 相關文件,而src/index.js文件爲組件入口:

咱們就寫一個爛大街的計數器的例子,經過加減按鈕實現數字的增減。

首先是store中的代碼:

actionTypes.js 用來統一存放 action 的 type 類型,並導出

// 增
export const ADD = "ADD";
// 減
export const REDUCE = "REDUCE";
複製代碼

reducer.js 建立 reducer 函數,並導出

import { ADD, REDUCE } from "./actionTypes";

const defaultVal = 0;

function reducer(state = defaultVal, action) {
  switch (action.type) {
    case ADD:
      let newVal1 = state + 1;
      return newVal1;
    case REDUCE:
      let newVal2 = state - 1;
      return newVal2;
    default:
      return state;
  }
}

export default reducer;

複製代碼

index.js 文件建立 store,並導出

import { createStore } from "redux";
import reducer from "./reducer";

const store = createStore(reducer);

export default store;

複製代碼

而後 src/index.js 文件,是組件入口:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { ADD, REDUCE } from "./store/actionTypes";
import store from "./store";

class APP extends Component {
  render() {
    return (
      <div>
        <div>{store.getState()}</div>
        <div>
          <button onClick={this.handleReduce.bind(this)}>-</button>
          <button onClick={this.handleAdd.bind(this)}>+</button>
        </div>
      </div>
    );
  }

  // 執行減操做
  handleReduce() {
    store.dispatch({
      type: REDUCE
    });
  }

  // 執行加操做
  handleAdd() {
    store.dispatch({
      type: ADD
    });
  }
}

ReactDOM.render(<APP />, document.getElementById("root"));

複製代碼

此時運行項目,在瀏覽器中實現加減操做。可是會發現並無用,怎麼點擊都沒有預想的效果。其實這裏少了關鍵的一步,在 react 中使用不一樣於第一個例子裏那樣,第一個例子中我 dispatch 後會從新獲取 state,也就是store.getState(),但在 react 組件裏使用時,能夠看到我只是獲取了一次 state,那麼我想要的效果是每當我 dispatch 後,這個 state 會自動更新。

redux 中提供了這樣的一個功能,就是store.subscribe(listener),它是 store 下的一個方法,會添加一個變化監聽器,每當 dispatch action 的時候就會執行。那麼有了監聽器,每次 dispatch 時咱們須要執行啥呢?沒錯,咱們給組件從新 render 下。修改ReactDOM.render這部分代碼,以下:

function render() {
  ReactDOM.render(<APP />, document.getElementById("root"));
}
render();

store.subscribe(render);
複製代碼

用一個函數包裹ReactDOM.render方法,並執行,這個函數其實就是 listener 了,把它傳給 subscribe,這樣每次 dispatch 後,就能夠更新 state 了。

以上就是結合 react 和 redux 的一個簡單入門小例子,我寫的不必定清楚,可是代碼敲一遍也能瞭解個大概。

補充

兩天後,我以爲仍是要補充介紹下 redux 三大原則:

  1. 整個應用只能有一個 store,即const store = createStore(reducer) 這樣的建立 store,只能存在一個,保證單一數據源;
  2. state 是隻讀的,惟一改變 state 的方法就是觸發 action。經過dispatch(action)來表達想要修改的意圖,全部的修改會被集中化處理;
  3. reducer 必須是純函數。前面也說過了,reducer 是接收先前的 state 和 action,並返回新的 state,而不是直接修改 state,這就是符合純函數的約束。

什麼是純函數?

  • 不能改寫參數,咱們是經過參數返回新的數值的,一樣的輸入,獲得一樣的輸出
  • 不能調用系統 I/O 的API
  • 不能調用Date.now()或者Math.random()等不純的方法,由於每次會獲得不同的結果

文中有寫的不對的地方,望有大佬指點。

相關文章
相關標籤/搜索