State Management

一些网店应用程序需要管理全局状态的方法。有许多状态管理库可以帮助您做到这一点,例如 ReduxMobX。Salesforce 并不认定一个特定的状态管理库强于任何其他库,您可以构建 PWA Kit 应用程序,而无需使用任何一个状态管理库。

您始终可以通过道具向组件提供状态信息,但如果您想使用 React 更高级的功能来管理状态,本指南还介绍其他方法。

使用道具 

在组件之间共享状态的最简单技术是通过道具将状态信息提供给组件。该组件必须是所有需要访问状态数据的组件的父级组件。React 文档详细描述这种技术并将其称为提升状态

使用 React Context API 

React 的 Context API简化了将数据提供给大量组件的过程,而无需在应用程序组件树的每个级别通过道具手动传递数据。

React Context 示例 

这是一个示例,说明如何将 React Context API 与组件一起使用,该组件充当所有组件的包装器,从而为其提供共享状态数据。

首先创建一个名为 GlobalState 的组件,该组件具有两个上下文对象:

  1. GlobalStateContext:您可以在其中存储全局状态数据。
  2. GlobalDispatchContext:您的 dispatch 函数,它通过 reducer 函数返回全局状态的更新快照。

每个上下文对象都带有一个 ’ 组件,该组件允许使用组件订阅上下文更改。在这里,上下文对象通过道具 initialStatedispatch 初始化。当您使用 useReducer Hook 将状态与 reducer 连接时,您将获得这些道具。有关 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 组件。

要获取道具对象,您需要先对其进行初始化。使用 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}

现在,您可以让任何组件访问您的全局上下文数据,包括更新能力。从您的 GlobalStateContext 组件导入 GlobalDispatchContextGlobalState 并为 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 方法允许您为状态管理库设置上下文提供程序。例如,您可以使用它将应用程序包装在 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