狀態管理

有些網店應用程式需要能夠管理全域狀態的方法。有許多狀態管理庫可以協助您達成這個目標,例如 ReduxMobX。Salesforce 並不為任何特定的狀態管理庫背書,您無需使用任何狀態管理庫,也能建置 PWA Kit 應用程式。

您可以透過 Props 向元件提供狀態資訊,但若您想使用 React 更為進階的功能來管理狀態,這份指南也會介紹其他方法。

使用 Props 

在元件之間分享狀態最簡單的方式,就是透過 Props 向元件提供狀態資訊。此元件必須是所有需要存取狀態資料的元件的父代元件。React 文件詳細說明了此技術,並將其稱為提升狀態

使用 React Context API 

React 的 Context API 簡化了將資料提供給大量元件的過程,而無需在應用程式元件樹的每個層級透過 Props 手動傳遞資料。

React Context 範例 

以下範例說明如何將 React Context API 與元件一起使用,來作為所有元件的包裝,為它們提供共用的狀態資料。

首先,先建立一個稱為 GlobalState 的元件,其中有兩個內容物件:

  1. GlobalStateContext:您可在此儲存全域狀態資料。
  2. GlobalDispatchContext:您的 dispatch 函式,可透過 reducer 函式傳回已更新的全域狀態快照。

每個上下文物件都帶有一個 『 元件,該元件允許使用元件訂閱上下文更改。在此,Context 物件會使用 Props initialStatedispatch 進行初始化。當您使用 useReducer Hook 連結狀態至 reducer 時,會獲得這些 Props。如需進一步瞭解 React Hook 的相關資訊,包括如何將它們與 Context API 一起使用,請參閱官方 React Hook 參考文件

以下說明如何定義並初始化使用 Context API 在網店應用程式中儲存全域狀態的元件:

1// app/components/_app-config/index.jsx
2
3import {createContext, useContext} from 'react'
4
5export const GlobalContext = createContext()
6export const useGlobalState = () => useContext(GlobalContext)
7
8const AppConfig = (props) => {
9    const [globalState, setGlobalState] = useState({
10        customer: {name: 'Samantha', registered: true},
11    })
12
13    return (
14        <GlobalContext.Provider value={(globalState, setGlobalState)}>
15            {props.children}
16        </GlobalContext.Provider>
17    )
18}
19
20// app/pages/page/index.jsx
21
22import React, {useContext} from 'react'
23import {useGlobalState} from 'components/_app-config'
24
25export const Page = () => {
26    const {customer, setGlobalState} = useGlobalState()
27
28    // Use setGlobalState to change the global state.
29
30    return <div>{customer.name}</div>
31}

位於 app/components/_app-config/index.jsxAppConfig 特殊元件是您可以初始化狀態管理系統的地方。在此,props.children 包含您應用程式的所有元件,因此您要在此處新增 GlobalState 元件。

要獲得 Props 物件,您必須先將其初始化。使用 React Hook useReducer 來建立連結狀態至 reducer。useReducer 函式會接受 initialState 物件和從 GlobalState 元件匯入的 reducer。接下來,請傳入已連結狀態和 dispatch 函式來修改該狀態。

舉例來說:

1// <PROJECT_DIR>/app/components/_app-config/index.jsx
2
3// Replace your existing React imports
4import React, { useState }, { useReducer }, from "react"
5
6// Import GlobalState component defined in previous example
7import GlobalState, { reducer } from "../global-state"
8
9const initialState = {
10  cart: null
11}
12
13// Combine this with your existing AppConfig component code
14function AppConfig(props) {
15  const [state, dispatch] = useReducer(reducer, initialState);
16
17  return (
18    <GlobalState initialState={state} dispatch={dispatch}>
19      {props.children}
20    </GlobalState>
21  )
22}

現在,您可以讓任何元件存取全域 Context 資料,包括進行更新的能力。從 GlobalState 元件匯入 GlobalStateContextGlobalDispatchContext,並提供這些 Context 物件給 useContext() 方法。

舉例來說:

1import React, {useContext} from 'react'
2import {GlobalStateContext, GlobalDispatchContext, SET_CART_ITEMS} from 'app/components/global-state'
3
4function ExamplePage() {
5    const globalState = useContext(GlobalStateContext)
6    const dispatch = useContext(GlobalDispatchContext)
7
8    // Use this where you want to update the cart data in your global state
9    dispatch({type: SET_CART_ITEMS, payload: cartItems})
10
11    return ( /* render something */ )
12}

使用狀態管理庫 

您可以覆寫位於 app/components/_app-config/index.jsx 的預設 AppConfig 特殊元件,來自訂應用程式以使用狀態管理庫。

Redux 範例 

以下是在 AppConfig 內為您的 PWA 設定 Redux 的方式。此元件有四種您需要使用的方法:

  1. restore 方法是您使用 reducer、初始狀態和任何中介軟體建立 Redux 儲存區的地方。。
  2. freeze 方法可讓您凍結狀態管理後端,以嵌入到頁面 HTML。
  3. extraGetPropsArgs 方法是您可以傳回任何您想在整個應用程式的 getProps 方法中插入的額外引數的地方。在這種情況下,我們可以將 Redux 儲存區作為額外引數傳回。
  4. render 方法可讓您為狀態管理庫設定 context 提供器。舉例來說,您可以使用它來將應用程式包裝在 Redux 提供器內。

以下是簡短範例,說明如何使用 AppConfig 方式為您的網店應用程式設定 Redux。除了我們於此處說明的步驟之外,完整的解決方案還需要您使用 reducer、初始狀態和任何所需的中介軟體來建立您的儲存區。

1// <PROJECT_DIR>/app/components/_app-config/index.jsx
2
3// Combine these imports with your existing imports to include Redux
4imports import React, { useState }, { useReducer }, from "react"
5import PropTypes from "prop-types"
6import { createStore } from "redux"
7import { Provider } from "react-redux"
8
9/**
10 * Combine this code with your existing AppConfig code to use Redux by injecting store object and dispatch function into all getProps() functions.
11 */
12
13class AppConfig extends React.Component {
14  static restore(locals, frozen = {}) {
15    // create your store with a reducer, initial state, and any middleware.
16    const initialState = isServerSide ? {} : frozen
17    locals.store = createStore(reducer, initialState, middlewares)
18  }
19  static freeze(locals) {
20    return locals.store.getState()
21  }
22  static extraGetPropsArgs(locals) {
23    return {
24      store: locals.store,
25      dispatch: locals.store.dispatch
26    }
27  }
28
29  /**
30   * The render function will wrap the app in a Redux provider.
31   */
32  render() {
33    const { children, locals } = this.props
34    return <Provider store={locals.store}>{children}</Provider>
35  }
36}
37
38AppConfig.propTypes = {
39  children: PropTypes.node,
40  locals: PropTypes.object
41}
42
43export default AppConfig