我不萌,但新。
2018年要結束了,這一年,我。。。算了,很少說了。
想起上半年看的 react,到如今也沒怎麼用過,都快忘光了,趁如今有時間我來捋捋,首先從 redux 下手(與 react 有啥什麼關係???😂)vue
我想學習 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,它用來處理數據,本質上是一個函數,有兩個參數 state 和 action,state 便是保存的狀態,若是不給他賦初始值,那麼它就等於undefined
,我在這裏給了它一個初始值 defaultState。action 便是行爲,它是一個對象,必須有一個名爲 type 的字段來表示將要執行的動做,如代碼裏的ADD
就是我傳入的一個表示添加的行爲,(注意type的值可任意,應儘可能語意化)。除了 type 字段外,你能夠任意添加本身須要的字段,好比這裏我傳裏一個 num 字段,我須要將它添加到 state 中去。
reducer的注意事項:
前面說了 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將 action 和 reducer 聯繫起來,維持應用的 state,能夠經過store.getState()
來獲取 state,經過 store.dispatch()
來更新 state。咱們經過引入 createStore 方法,並傳入 reducer 爲第一個參數來建立 store。
前面介紹了 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 三大原則:
const store = createStore(reducer)
這樣的建立 store,只能存在一個,保證單一數據源;dispatch(action)
來表達想要修改的意圖,全部的修改會被集中化處理;什麼是純函數?
- 不能改寫參數,咱們是經過參數返回新的數值的,一樣的輸入,獲得一樣的輸出
- 不能調用系統 I/O 的API
- 不能調用Date.now()或者Math.random()等不純的方法,由於每次會獲得不同的結果
文中有寫的不對的地方,望有大佬指點。