自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用 Redux 做狀態(tài)管理,真的很簡單!

開發(fā) 前端
本文通過實際案例反向釋義 Redux 中的名詞概念,同時借助 @reduxjs/toolkit 模塊簡化 Redux 的使用,希望通過今天的分享可以幫助大家打開心結(jié),抱抱 Redux。

最近在某項目中欲選一工具用作項目的全局狀態(tài)管理,通過綜合比較考慮,最終選擇了 Redux。都說 Redux 難上手,今天通過 1 個案例, 3 個函數(shù)幫小伙伴們快速掌握并實踐生產(chǎn)!

作為一名前端工程師,不少小伙伴對于 Redux 的概念、使用可能還是比較模糊,上手使用的心智負擔(dān)也比較重!

但通過調(diào)研,目前 Redux 的生態(tài)可以說是非常豐富,這也使得將其引入作為項目的狀態(tài)管理工具庫變得 更加容易。

本文通過實際案例反向釋義 Redux 中的名詞概念,同時借助 @reduxjs/toolkit 模塊簡化 Redux 的使用,希望通過今天的分享可以幫助大家打開心結(jié),抱抱 Redux,提升工作效率,從此不加班!

一、Redux 基礎(chǔ)

一開始就闡釋概念名詞,可能會增加大家上手的難度,因此該部分只對 Redux 做最基本的一個認識。

1、 什么是 Redux ?

Redux 是 JavaScript 狀態(tài)容器,提供 可預(yù)測、可調(diào)試、集中式 的狀態(tài)管理。

2、特點

  • 可預(yù)測: 讓你開發(fā)出 行為穩(wěn)定可預(yù)測、可運行在不同環(huán)境 (客戶端、服務(wù)端和原生程序)、且 易于測試 的應(yīng)用。
  • 集中管理: 集中管理應(yīng)用的狀態(tài)和邏輯可以讓你開發(fā)出強大的功能,如 撤銷/重做、 狀態(tài)持久化 等等。
  • 可調(diào)試: Redux DevTools 讓你 輕松追蹤 到 應(yīng)用的狀態(tài)在何時、何處以及如何改變。Redux 的架構(gòu)會記下每一次改變,借助于 "時間旅行調(diào)試",你甚至可以把完整的錯誤報告發(fā)送給服務(wù)器。
  • 靈活: Redux 可與任何 UI 層框架搭配使用,它體小精干(只有 2kB,包括依賴),并且有 龐大的插件生態(tài) 來實現(xiàn)你的需求。

3、 設(shè)計思想

Redux 既然是狀態(tài)管理庫,那么接下來掌握一下基本的數(shù)據(jù)流概念和原則。

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

整個應(yīng)用的 全局 state 被儲存在一棵對象樹(object tree)中,并且這個對象樹只存在于唯一 Store(存儲) 中。

單一數(shù)據(jù)源使得同構(gòu)應(yīng)用開發(fā)變得容易,將狀態(tài)在統(tǒng)一的 對象樹 中維護管理也會更加容易!

(2) 單向數(shù)據(jù)流(one-way data flow)

Redux 單向數(shù)據(jù)流

  1. 用 state 來描述應(yīng)用程序在特定時間點的狀況。
  2. 基于 state 來渲染出 View。
  3. 當(dāng)發(fā)生某些事情時(例如用戶單擊按鈕),state 會根據(jù)發(fā)生的事情進行更新,生成新的 state。
  4. 基于新的 state 重新渲染 View。

(3) 不可變性(Immutability)

對于狀態(tài)(state)的描述一般都是一個大的 JavaScript 對象(Object Tree),例如:

const state = {
isLoading: true,
userInfo: {
uid: 1,
wechat: 'DYBOY2020',
phone: 177****7777,
history: [1,2,3,4,5]
}
}

由于 JS 的動態(tài)性,使得對象是可以修改的,Redux 想要記錄每一個狀態(tài),如果直接修改 state 中的引用類型屬性,勢必會導(dǎo)致 state 的變化不可追溯和預(yù)測。

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

Redux 期望所有狀態(tài)更新都是使用不可變的方式,因此,每一次的 state 變更,不會修改原對象,而是修改前一個狀態(tài)(state)的克隆對象,以此來保證不可變性和正確性,同時記錄每一次變化的 state。

(4) 純函數(shù)更新 state

純函數(shù): 相同的輸入,總是會得到相同的輸出,并且在執(zhí)行過程中沒有任何副作用的函數(shù)。

為了保證數(shù)據(jù)的改變正確性,以及滿足 state 不可變性的要求,因此引入了 純函數(shù) 作為更新狀態(tài)的唯一方式。

React Hooks 的狀態(tài)管理就融合了 Redux 的設(shè)計思想,畢竟把 Redux 的作者 Dan Abramov 都直接挖過去了!

二、案例實踐

下面講講如何接入一個全新的項目中,以 create-react-app[1] 腳手架創(chuàng)建的項目為例子。

借助 @redux/toolkit,不再需要刻意關(guān)心如何組織編寫 Reducer、Action creator、Action Type 等內(nèi)容,同時,默認就融合支持 異步 Thunks。

再結(jié)合 React 16.x 中的 Hooks,使用 useSelector()、useDispatch() 在任意組件中消費 Store。

1、初始化項目

首先是借助 create-react-app 初始化一個 TS + React 環(huán)境的項目。

npx create-react-app craapp --template typescript

2、安裝 Redux 相關(guān)依賴

yarn add redux react-redux @reduxjs/toolkit
  • redux: 核心狀態(tài)管理庫。
  • react-redux: 用于 React 框架的橋接層。
  • @reduxjs/toolkit: 降低 Redux 使用難度的助手。

3、 全局 Store 的創(chuàng)建

所有的狀態(tài)都放在了 Store 中,因此需要一個統(tǒng)一的地方來管理,以一個計數(shù)器為例,在 ./src/store 下的文件結(jié)構(gòu)如下:

.
├── index.ts // store 實例,導(dǎo)出 statedispatch 類型
└── reducers // 集合所有的 reducer
├── counter.ts // 用于計數(shù)器的 reducer、action、selector
└── index.ts // 導(dǎo)出 rootReducers,用于整合所有的 reducer

(1) store/index.ts

import { configureStore } from "@reduxjs/toolkit";
import rootReducers from "./reducers"; // 引入 reducer 的集合
// 實例化 store,全局唯一
const store = configureStore({
reducer: rootReducers,
});
// 導(dǎo)出 Store 中的狀態(tài)(state)類型
export type RootState = ReturnType<typeof store.getState>;
// 導(dǎo)出更改狀態(tài)的 Dispatch 方法類型
export type AppDispatch = typeof store.dispatch;
// 默認導(dǎo)出 store,用于全局的 Provieder 消費
export default store;

(2) store/reducers/index.ts

import {combineReducers} from '@reduxjs/toolkit'
import counterSlice from './counter' // 可以引入各種 reducer
const rootReducers = combineReducers({
counter: counterSlice // 這里通過 MAP 形式,自定義不同 reducer 的“命名空間”
// ... 可以在這里擴展添加任意的 reducer
})
// 默認導(dǎo)出,給 configureStore 消費
export default rootReducers

(3) store/reducers/counter.ts

接下來看看怎么便捷的創(chuàng)建一個 Reducer,以前使用 Redux 總是需要手動創(chuàng)建多個文件,reducer、action、action creator,但現(xiàn)在可以直接借助 @redux/toolkit 統(tǒng)一的放在一個文件中,結(jié)構(gòu)化的去描述 Redux 中的 action 和 redcuer。

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { AppDispatch, RootState } from ".."; // 在 store/index.ts 中聲明的類型
// 借助 createSlice 創(chuàng)建 reduceraction
const CounterSlice = createSlice({
name: "counter", // 生成 Action type 的前綴,例如:counter/increment
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1; // 這里默認通過了 immer 處理,不會修改原 state
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
decrementByAmount: (state, action: PayloadAction<number>) => {
state.value -= action.payload;
},
},
});
// Action Creator 用于執(zhí)行返回描述如何更新 stateAction
export const { increment, decrement, incrementByAmount, decrementByAmount } =
CounterSlice.actions;
// 異步 thunk,用于需要在更新數(shù)據(jù)前異步處理數(shù)據(jù)的情況
export const incrementAsync = (amount: number) => (dispatch: AppDispatch) => {
setTimeout(() => {
dispatch(incrementByAmount(amount));
}, 1500);
};
// Selector,作為 useSelector 讀取數(shù)據(jù)的函數(shù)參數(shù)
export const counterSelector = (state: RootState) => state.counter.value;

// Reducer,真正執(zhí)行修改 state 的純函數(shù)
export default CounterSlice.reducer;

如上的寫法可以作為一種“模板”,毋須關(guān)心各種概念之間的組合,直接用就可以了!

console.log(CounterSlice)
/*
output:
{
name: 'counter',
actions : {
increment,
decrement,
incrementByAmount,
decrementByAmount
},
reducer
}
*/

上述 actions 中的函數(shù)就是 Action creator,例如執(zhí)行 increment() 返回的就是:

{type: 'counter/increment'}

執(zhí)行 incrementByAmount(5) 返回的是:

{type: 'counter/incrementByAmount', payload: 5}

4、組件中讀寫 Stoe

首先是需要將 Store 實例綁定到我們的應(yīng)用上。

在 ./src/index.tsx 中添加如下:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux"; // 引入 Provider,綁定 store 到應(yīng)用上
import store from "./store"; // 引入 store 實例
import App from "./App";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}> {/* 綁定 store */}
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);

結(jié)合 react-redux 提供的 useSelector() 和 useDispatch() 可以在我們自定義的 Counter 組件中消費 counter 狀態(tài)(數(shù)據(jù))。

//文件位置:src/pages/counter/index.tsx
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { decrement, incrementAsync, counterSelector } from "@/store/reducers/counter";
import "./index.scss";
const CounterPage = () => {
const count = useSelector(counterSelector) // 讀取 count
const dispatch = useDispatch() // 獲得 dispatch,結(jié)合 action 就可更新 state
return (
<div className="counter-page">
<div className="counter">
{/* 同步 - */}
<div className="btn" onClick={() => dispatch(decrement())}>
-
</div>
<div className="val">{`${count}`}</div>
{/* 異步 + */}
<div className="btn" onClick={() => dispatch(incrementAsync(5))}>
+
</div>
</div>
</div>
);
};

export default CounterPage;

實際效果:

計時器效果演示

縱觀整個案例,相較于不使用 @redux/toolkit 顯著提升了研發(fā)的效率,降低了研發(fā)的使用心智負擔(dān)!

三、擴展知識

1、 @redux/toolkit API

在上述的實際案例中,用到了如下 API:

  • configureStore(): 簡化 Store 的創(chuàng)建,默認創(chuàng)建了執(zhí)行異步的中間件,自動啟用 redux devtool。
  • combineReducers(): 簡化合并 reducer 的操作,并自動注入 state 和 action。
  • createSlice(): 簡化并統(tǒng)一創(chuàng)建 action creator、reducer。

上述仨 API 可以滿足大部分的場景,在此工具輔助下,極大程度上減少了 TypeScript 類型定義的工作。

當(dāng)然,想要了解更多關(guān)于 @redux/toolkit 便捷的 API,推薦閱讀官方文檔:

  • @redux/tookit 的 API 使用手冊[2]。
  • @redux/tookit 的 API 使用手冊 —— TypeScript 類型相關(guān)[3]。

2、 Redux 的狀態(tài)變更

如果對 Redux 的狀態(tài)更新過程和原理感興趣,這里十分推薦閱讀:

  • Redux如何實現(xiàn)state變化觸發(fā)頁面渲染?[4]。

3、 Redux 的同步和異步數(shù)據(jù)流

同步數(shù)據(jù)流:

Redux 的同步數(shù)據(jù)流動圖鏈接:https://umapu.cn/imgs/202203/8c767817cfd66ba6c45276c52e98c8b2.gif。

異步數(shù)據(jù)流:

Redux 的異步數(shù)據(jù)流動圖鏈接:https://umapu.cn/imgs/202203/e7edf1f729772323b2aebaae824716eb.gif。

四、總結(jié)

React 項目選擇 Redux 作為全局的狀態(tài)管理還是非常推薦的,結(jié)合 React 16.x 的 Hooks 狀態(tài)更新,非常方便,也符合函數(shù)組件的編碼風(fēng)格,再瞅瞅 React 的 useContext 和 useReducer,是不是會有一種 React 和 Redux 就是倆親兄弟的感覺?

簡單總結(jié)一下:

  1. 推薦在 React 項目中使用 Redux 作為狀態(tài)管理。
  2. 需要掌握 Redux 中的設(shè)計思想。
  3. 推薦使用 @redux-toolkit,可降低心智負擔(dān),顯著提升研發(fā)效率。
  4. 當(dāng)掌握 @redux-toolkit 后,可補充閱讀 Redux 原本的 API,思考一下為什么 @redux-toolkit 要這么做?

參考資料

[1]create-react-app: https://create-react-app.dev。

[2]@redux/tookit 的 API 使用手冊: https://redux-toolkit.js.org/usage/usage-guide。

[3]@redux/tookit 的 API 使用手冊 —— TypeScript 類型相關(guān): https://redux-toolkit.js.org/usage/usage-with-typescript。

[4]Redux如何實現(xiàn)state變化觸發(fā)頁面渲染?: https://juejin.cn/post/6945808822308962317。

責(zé)任編輯:姜華 來源: DYBOY
相關(guān)推薦

2021-08-31 10:52:30

容量背包物品

2022-02-14 21:31:00

用戶身份驗證

2017-03-16 16:57:56

2024-07-15 08:02:37

2012-05-22 14:16:26

Linux運維

2024-04-22 09:12:39

Redux開源React

2019-11-05 09:20:06

SQLiteLinux

2013-04-08 15:39:15

程序員

2010-02-23 16:21:24

Python Win

2010-03-02 17:22:46

Android技術(shù)

2018-10-30 12:44:04

Linux系統(tǒng)內(nèi)存

2016-04-21 09:43:33

編程音樂

2022-08-27 10:49:59

北斗物聯(lián)網(wǎng)

2010-06-12 10:10:55

2010-01-20 10:14:53

C++程序

2013-12-11 09:29:02

2015-08-07 13:54:07

H5

2010-10-09 16:51:47

2021-08-14 08:45:27

React開發(fā)應(yīng)用程序

2021-09-28 09:00:00

開發(fā)JavaScript存儲
點贊
收藏

51CTO技術(shù)棧公眾號