文章目錄
- 一,簡介
- 二,安裝
- 三,三大核心概念Store、Action、Reducer
- 3.1 Store
- 3.2 Reducer
- 3.3 Action
- 四,開始函數式組件中使用
- 4.1,引入store
- 4.1,store.getState()方法
- 4.3,store.dispatch()方法
- 4.4,store.subscribe()方法
- 五,Redux 的三大原則
一,簡介
Redux 是 JavaScript 應用的狀態容器,提供可預測的狀態管理。
它主要的幾個方法如下:
重要的有方法 有 dispatch(分發action)
、getState(獲取state)
、subscribe(監聽state的變化)
,下面會介紹到,另外兩個可以不用管;
那什么時候使用Redux呢?
當遇到如下問題時,建議開始使用 Redux
:
- 你有很多數據隨時間而變化
- 你希望狀態有一個唯一確定的來源(single source of truth)
- 你發現將所有狀態放在頂層組件中管理已不可維護
二,安裝
我這里安裝的是 "redux": "^4.2.1"
版本;
npm install redux --save
項目的src目錄下面新建store文件夾
和index.js,reducer.js
;如下:
三,三大核心概念Store、Action、Reducer
3.1 Store
Store:存儲數據的地方。最好整個應用只有一個 Store。
createStore()
:用來生成 Store。接收 Reducer 作為其參數。
index.js
/*** 引入createStore 專門創建最為核心的store對象* 目前createStore已經棄用,所以我們要引用legacy_createStore */iimport { legacy_createStore } from "redux";
import reducer from './reducer.ts'// 創建數據倉庫 引入reducer函數進行對數據的處理
const store = legacy_createStore(reducer)export default store
3.2 Reducer
reduce的本質就是一個函數 ,作用是初始化狀態和加工狀態。
reduce函數里面接收兩個參數,第一個參數是state的初始值,第二個參數是一個action對象,對象里的第一個屬性是type
也就是函數的名稱,第二個屬性就是傳進來的值,用于后續更改state;
reducer.ts
// 約束類型
interface Eula {name: string;age: number;
}
// 定義數據
const defaultState: Eula = {name: "Eula",age: 18
};// reducer 函數 用于更改數據
let reducer = (preState = defaultState, action: { type: string; data: number }) => {// action解構出來let { type, data } = action;// 第一種寫法 每個分支使用return進行返回// switch (type) {// case "update_age":// preState.age = data;// return preState;// case "add_age":// preState.age++;// return preState;// case "del_age":// preState.age--;// return preState;// default:// return preState; // 初始化時// }// 第二種寫法 break 與最終的return返回結果switch (type) {case "update_age":preState.age = data;break;case "add_age":preState.age++;break;case "del_age":preState.age--;break;default:preState; // 初始化時}return preState; // 此處 一定要使用return進行返回最終改變的值
};export default reducer;
注意: 初次加載 Store 會自動調用一次 Reducer 進行初始化狀態,此時 state 是 undefined,action 對象中的 type 為 @@redux/INITxxx
。手動調用 store.dispatch()
也會觸發 Reducer 的自動執行。
3.3 Action
Action 就是一個普通的 JS 對象,用于描述要更新的數據類型和內容,其中 type 屬性是必須的,表示 Action 的名稱,其他屬性可以自由設置。
redux.tsx
// 引入store
import store from "../../../store/index";
// 更改數據時調用
store.dispatch({ type: "update_age", data: 100 });
store.dispatch()
:所有數據的變化,必須通過派發(dispatch) Action 來更新。接受一個 Action 對象作為參數,將其發送出去。
四,開始函數式組件中使用
redux.tsx
import React, { useState } from "react";
// 1,引入store
import store from "../../../store/index";// 渲染數據
const myList:[] = [];const Redux: React.FC = () => {let [list, setList] = useState(myList);console.log("store:", store);// 監聽數據的變化const unsubscribe = store.subscribe(() => {console.log("訂閱數據的變化", store.getState());// 此處用來觸發視圖的更新setList([]);});// 改變store中的數據const update = () => {store.dispatch({ type: "update_age", data: 100 });};const add = () => {store.dispatch({ type: "add_age" });};const del = () => {store.dispatch({ type: "del_age" });};// 此處才是真正渲染的頁面return (<div className="redux"><h3>redux演示</h3><button onClick={update}>更改store的數據+100</button><button onClick={add}>更改store的數據++</button><button onClick={del}>更改store的數據--</button><p>store的num數據:{store.getState().age}</p></div>);
};
export default Redux;
效果圖:
上面的組件是一個簡單的案例演示,定義了三個點擊事件,點擊第一個按鈕state.age+100
,點擊第二個按鈕每次state.age+1
,點擊第三個按鈕age每次減一;下面會詳細介紹幾個重點內容:
4.1,引入store
先引進來,這個沒什么好說的;
import store from "../../../store/index";
4.1,store.getState()方法
getState()
方法是redux實例下的方法之一,上面的第一張截圖已經通過store實例打印出來了;
getState()
的作用是獲取當前狀態下運行在redux中的state;也就是說獲取store中最新的數據;
<p>store的num數據:{store.getState().age}</p>
4.3,store.dispatch()方法
dispatch()
是唯一能夠修改 state 數據的行為。通過分發action
(其實就是一個對象),配合 dispatch 函數傳入的 action 及其 payload 計算得到新的 state,并更新到閉包數據中,這樣就實現了 state 的更新;
如下:
reducer.tsx
// 改變store中的數據const update = () => {store.dispatch({ type: "update_age", data: 100 });};const add = () => {store.dispatch({ type: "add_age" });};const del = () => {store.dispatch({ type: "del_age" });};
上面的代碼會和下面的 switch case
表達式所判斷的type
要一 一對應,用于更新state;
reducer.ts
let reducer = (preState = defaultState, action: { type: string; data: number }) => {let { type, data } = action;// 第一種寫法 每個分支使用return進行返回// switch (type) {// case "update_age":// preState.age = data;// return preState;// case "add_age":// preState.age++;// return preState;// case "del_age":// preState.age--;// return preState;// default:// return preState; // 初始化時// }// 第二種寫法 break 與最終的return返回結果switch (type) {case "update_age":preState.age = data;break;case "add_age":preState.age++;break;case "del_age":preState.age--;break;default:preState; // 初始化時}return preState; // 此處 一定要使用return進行返回最終改變的值
};
上面的兩種寫法是一樣的;對比一下;
4.4,store.subscribe()方法
subscribe
函數只要store中的state數據變化了,就會觸發subscribe
方法,相當注冊了一個監聽器;監聽store中的數據變化;
從 react 層面來說,redux 的 store 是隔離開的,我們需要一個橋梁,使得數據層出現更新的同時更新UI層邏輯,這時 store 中的最后一個方法,subscribe 方法就派上用場了。
注意: setList([])
:是為了主動觸發react視圖更新的方法,否則store中數據改變了,視圖卻沒有重新渲染。
import React, { useState } from "react";
const Redux: React.FC = () => { let [list, setList] = useState(myList);
// 監聽數據的變化const unsubscribe = store.subscribe(() => {console.log("訂閱數據的變化", store.getState());// 此處用來觸發視圖的更新setList([]);});
}
subscribe
也同時返回了一個 unsubscribe 函數。當我們不在希望訂閱這個監聽器時,調用 unsubscribe()
,對應的函數就會從監聽器隊列中被移除。
unsubscrib() // 不再監聽
五,Redux 的三大原則
- 單一數據源:整個應用程序的 State 被存儲在一棵 object tree 中,并且這棵 object tree 只存儲在一個 Store 中。單一數據源可以讓整個應用程序的 State 變得方便維護、修改、追蹤。
- State 是只讀的:唯一修改 State 的方法就是觸發 Action,不要試圖在其他地方通過任何的方式來修改State。這樣可以保證所有的修改都被集中化處理,并且按照嚴格的順序來執行。
- 使用純函數來執行修改:通過 Reducer 將舊的 State 和 Action 聯系在一起,返回一個新的 State。所有的Reducer 都應該是純函數,不能產生任何的副作用。
End:
[redux中文網]: https://cn.redux.js.org/