Redux 基礎,第 1 部分:Redux 概觀
Redux 基礎,第 1 部分:Redux 概觀
- Redux 是什麼,以及您可能想要使用它的原因
- 構成 Redux 應用程式的基本部分
簡介
歡迎使用 Redux 基礎教學!本教學將向您介紹使用 Redux 的核心概念、原則和模式。完成後,您應該了解構成 Redux 應用程式之不同部分、使用 Redux 時的資料流程,以及我們建立 Redux 應用程式的標準建議模式。
在本教學課程的第一部分,我們將簡要地探討一個運作中的 Redux 應用程式的最小範例,以了解其組成部分,而在 第二部分:Redux 概念與資料流程 中,我們將更詳細地探討這些組成部分,以及資料如何在 Redux 應用程式中流動。
從 第三部分:狀態、動作和簡化函式 開始,我們將運用這些知識來建立一個小型範例應用程式,說明這些組成部分如何結合在一起,並探討 Redux 在實際應用中的運作方式。在我們「手動」完成運作範例應用程式的建置,以便您能確切了解發生了什麼事之後,我們將探討一些通常與 Redux 搭配使用的標準模式和抽象化。最後,我們將了解這些低階範例如何轉換為我們建議在實際應用程式中實際使用的較高階模式。
如何閱讀本教學課程
本教學課程將教導您「Redux 的運作方式」,以及為何這些模式存在。
請注意,本教學課程故意顯示較舊的 Redux 邏輯模式,這些模式需要比我們教導的「現代 Redux」模式(使用 Redux Toolkit)更多的程式碼,才能建置使用 Redux 的應用程式,目的是為了說明 Redux 背後的原則和概念。它並非旨在成為可供實際使用的專案。
請參閱這些網頁,以了解如何使用 Redux Toolkit 的「現代 Redux」
- 完整的「Redux Essentials」教學課程,教導「如何正確使用 Redux」,使用 Redux Toolkit 來建立實際世界的應用程式。我們建議所有 Redux 學習者都應該閱讀「Essentials」教學課程!
- Redux Fundamentals,第 8 部分:使用 Redux Toolkit 的現代 Redux,說明如何將前幾節中的低階範例轉換為現代 Redux Toolkit 等效項
一旦您了解所有組成部分如何結合在一起,我們將探討如何使用 Redux Toolkit 來簡化事情。Redux Toolkit 是使用 Redux 建置實際應用程式的建議方式,並建立在我們將在本教學課程中探討的所有概念之上。一旦您了解這裡涵蓋的核心概念,您將了解如何更有效地使用 Redux Toolkit。
我們已盡量讓這些說明適合初學者,但我們確實需要對您已知的內容做出一些假設,以便我們能專注於說明 Redux 本身。本教學課程假設您知道
- 熟悉 HTML 和 CSS。
- 熟悉 ES2015 語法和功能
- 了解 陣列和物件擴散運算子
- 了解 React 術語:JSX、狀態、函式元件、屬性 和 Hook
- 了解 非同步 JavaScript 和 發出 AJAX 請求
如果您對這些主題還不熟悉,我們建議您先花點時間熟悉它們,然後再回來了解 Redux。我們會在您準備好的時候等著您!
最後,您應該確保已在瀏覽器中安裝 React 和 Redux DevTools 擴充功能
- React DevTools 擴充功能
- Redux DevTools 擴充功能
Redux 是什麼?
首先,了解這個「Redux」是什麼很重要。它做了什麼?它能幫我解決什麼問題?為什麼我要使用它?
Redux 是一種使用稱為「動作」的事件來管理和更新應用程式狀態的模式和函式庫。它作為一個集中儲存庫,用於儲存整個應用程式中需要使用的狀態,並透過規則確保只能以可預測的方式更新狀態。
為什麼我應該使用 Redux?
Redux 可幫助您管理「全域」狀態,也就是應用程式許多部分需要的狀態。
Redux 提供的模式和工具可以更輕鬆地了解應用程式中的狀態在何時、何地、為何以及如何更新,以及應用程式邏輯在這些變更發生時將如何運作。Redux 引導您撰寫可預測且可測試的程式碼,這有助於讓您確信應用程式將按預期運作。
我應該在什麼時候使用 Redux?
Redux 可幫助您處理共用狀態管理,但就像任何工具一樣,它有其權衡。有更多概念要學習,還有更多程式碼要撰寫。它也會為您的程式碼增加一些間接性,並要求您遵循某些限制。這是短期和長期生產力之間的權衡。
Redux 在以下情況下更有用
- 應用程式中許多地方都需要大量的應用程式狀態
- 應用程式狀態會隨著時間頻繁更新
- 更新該狀態的邏輯可能很複雜
- 應用程式有中型或大型程式碼庫,而且可能由許多人共同開發
並非所有應用程式都需要 Redux。花點時間思考您正在建構的應用程式類型,並決定哪些工具最適合協助您解決正在處理的問題。
如果您不確定 Redux 是否是應用程式的理想選擇,這些資源提供了一些額外指引
Redux 函式庫和工具
Redux 是小型獨立的 JS 函式庫。不過,它通常與其他幾個套件搭配使用
React-Redux
Redux 可以與任何 UI 架構整合,最常與 React 搭配使用。 React-Redux 是我們的官方套件,讓您的 React 元件可以透過讀取狀態片段和發送動作來更新儲存庫,進而與 Redux 儲存庫互動。
Redux Toolkit
Redux Toolkit 是我們建議用於撰寫 Redux 邏輯的方法。它包含我們認為對於建構 Redux 應用程式至關重要的套件和函式。Redux Toolkit 建構在我們的建議最佳實務中,簡化大部分 Redux 任務、防止常見錯誤,並讓撰寫 Redux 應用程式變得更容易。
Redux DevTools Extension
Redux DevTools Extension 會隨著時間顯示 Redux 儲存庫中狀態變更的歷程記錄。這讓您可以有效除錯應用程式,包括使用「時間旅行除錯」等強大技術。
Redux 基礎知識
現在您已了解 Redux 是什麼,讓我們簡要了解組成 Redux 應用程式的部分,以及它的運作方式。
此頁面說明的其餘部分僅專注於 Redux 核心函式庫(redux
套件)。我們會在完成本教學課程的其餘部分時討論其他與 Redux 相關的套件。
Redux 儲存庫
每個 Redux 應用程式的核心都是儲存庫。一個「儲存庫」是一個容器,用於存放應用程式的全域狀態。
儲存庫是一個 JavaScript 物件,具有一些特殊功能和能力,使其與一般全域物件不同
- 您絕不可直接修改或變更儲存在 Redux 儲存庫中的狀態
- 相反地,造成狀態更新的唯一方法是建立一個一般動作物件,用來描述「應用程式中發生的事」,然後將動作派送至儲存庫,以告知儲存庫發生了什麼事。
- 當動作被派送時,儲存庫會執行根簡化器函式,並讓它根據舊狀態和動作計算新的狀態
- 最後,儲存庫會通知訂閱者狀態已更新,以便使用新資料更新 UI。
Redux 核心範例應用程式
讓我們來看看一個 Redux 應用程式的最小工作範例 - 一個小型計數器應用程式
由於 Redux 是不具備任何相依性的獨立 JS 函式庫,因此此範例僅透過載入 Redux 函式庫的單一腳本標籤撰寫,並使用基本的 JS 和 HTML 作為 UI。在實際應用中,Redux 通常會透過從 NPM 安裝 Redux 套件來使用,而 UI 則會使用像React這樣的函式庫建立。
第 5 部分:UI 和 React展示如何將 Redux 和 React 結合使用。
讓我們將此範例分解成其個別部分,以了解發生了什麼事。
狀態、動作和簡化器
我們首先定義一個初始狀態值來描述應用程式
// Define an initial state value for the app
const initialState = {
value: 0
}
對於此應用程式,我們將追蹤一個單一數字,其中包含計數器的目前值。
Redux 應用程式通常會將 JS 物件作為狀態的根部分,並將其他值包含在該物件中。
接下來,我們定義一個reducer 函式。reducer 接收兩個參數,目前的 state
和描述事件的 action
物件。當 Redux 應用程式啟動時,我們還沒有任何狀態,因此我們提供 initialState
作為此 reducer 的預設值
// Create a "reducer" function that determines what the new state
// should be when something happens in the app
function counterReducer(state = initialState, action) {
// Reducers usually look at the type of action that happened
// to decide how to update the state
switch (action.type) {
case 'counter/incremented':
return { ...state, value: state.value + 1 }
case 'counter/decremented':
return { ...state, value: state.value - 1 }
default:
// If the reducer doesn't care about this action type,
// return the existing state unchanged
return state
}
}
Action 物件總是有 type
欄位,這是你提供的字串,用作 action 的唯一名稱。type
應為可讀的名稱,以便任何查看此程式碼的人都能理解其含義。在本例中,我們使用單字「counter」作為 action 類型的前半段,後半段是「發生了什麼事」的描述。在本例中,我們的「counter」已「遞增」,因此我們將 action 類型寫成 'counter/incremented'
。
根據 action 的類型,我們需要回傳一個全新的物件作為新的 state
結果,或在不應變更任何內容的情況下回傳現有的 state
物件。請注意,我們透過複製現有狀態並更新副本,而不是直接修改原始物件,以不可變的方式更新狀態。
Store
現在我們有了 reducer 函式,我們可以透過呼叫 Redux 函式庫的 createStore
API 來建立一個store 實例。
// Create a new Redux store with the `createStore` function,
// and use the `counterReducer` for the update logic
const store = Redux.createStore(counterReducer)
我們將 reducer 函式傳遞給 createStore
,它使用 reducer 函式來產生初始狀態,並計算任何未來的更新。
UI
在任何應用程式中,使用者介面都會在螢幕上顯示現有狀態。當使用者執行某項操作時,應用程式會更新其資料,然後使用這些值重新繪製 UI。
// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')
// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
const state = store.getState()
valueEl.innerHTML = state.value.toString()
}
// Update the UI with the initial data
render()
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render)
在此小型範例中,我們只使用一些基本的 HTML 元素作為我們的 UI,並使用單一 <div>
顯示目前的數值。
因此,我們撰寫一個函式,它知道如何使用 store.getState()
方法從 Redux store 取得最新狀態,然後取得該值並更新 UI 以顯示它。
Redux store 讓我們可以呼叫 store.subscribe()
並傳遞一個訂閱者回呼函式,該函式會在每次更新 store 時被呼叫。因此,我們可以將我們的 render
函式傳遞為訂閱者,並知道每次更新 store 時,我們都可以使用最新值更新 UI。
Redux 本身是一個獨立的函式庫,可以在任何地方使用。這也表示它可以用於任何 UI 層。
發送 Action
最後,我們需要透過建立描述發生事件的action 物件,並將它們發送到 store,來回應使用者的輸入。當我們呼叫 store.dispatch(action)
時,store 會執行 reducer,計算更新後的狀態,並執行訂閱者以更新 UI。
// Handle user inputs by "dispatching" action objects,
// which should describe "what happened" in the app
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})
document.getElementById('decrement').addEventListener('click', function () {
store.dispatch({ type: 'counter/decremented' })
})
document
.getElementById('incrementIfOdd')
.addEventListener('click', function () {
// We can write logic to decide what to do based on the state
if (store.getState().value % 2 !== 0) {
store.dispatch({ type: 'counter/incremented' })
}
})
document
.getElementById('incrementAsync')
.addEventListener('click', function () {
// We can also write async logic that interacts with the store
setTimeout(function () {
store.dispatch({ type: 'counter/incremented' })
}, 1000)
})
在這裡,我們將調度動作,讓 reducer 將 1 加到目前的計數器值,或從中減去 1。
我們也可以撰寫程式碼,僅在特定條件為真時才調度動作,或撰寫一些非同步程式碼,在延遲後調度動作。
資料流程
我們可以使用此圖表來總結資料透過 Redux 應用程式的流程。它表示
- 動作如何根據使用者互動(例如點擊)進行調度
- 儲存執行 reducer 函式以計算新狀態
- UI 讀取新狀態以顯示新值
(如果這些部分現在還不太清楚,請別擔心!在您完成本教學課程的其餘部分時,請將這張圖片記在腦海中,您將會看到這些部分如何組合在一起。)
您已學到的內容
那個計數器範例很小,但它確實顯示了實際 Redux 應用程式的所有工作部分。我們在後續章節中討論的所有內容都是針對這些基本部分進行擴充。
基於此,讓我們回顧一下我們到目前為止學到的內容
- Redux 是用於管理全域應用程式狀態的函式庫
- Redux 通常與 React-Redux 函式庫一起使用,以將 Redux 和 React 整合在一起
- Redux Toolkit 是撰寫 Redux 邏輯的建議方法
- Redux 使用多種類型的程式碼
- 動作是具有
type
欄位的純粹物件,並描述應用程式中「發生了什麼事」 - Reducer 是根據先前狀態 + 動作來計算新狀態值的函式
- Redux 儲存會在調度動作時執行根 reducer
- 動作是具有
下一步?
現在您已了解 Redux 應用程式的基本部分,請進一步閱讀 第 2 部分:Redux 概念和資料流程,我們將更詳細地探討資料如何流經 Redux 應用程式。