v3 へのアップグレード

v3 では、@salesforce/retail-react-app を npm 依存関係として追加し、プロジェクトで拡張テンプレートを有効にすることで、@salesforce/retail-react-app の更新を利用できるようになりました。

このガイドでは、PWA Kit プロジェクトを v2.7.x から v3.0.0 に更新する方法について説明します。

新機能 

PWA Kit v3 では、以下を含む多くの新機能が追加されました。

⚛️ 必須:

  • getProps の破壊的変更 (breaking change)
  • React 18Node 18Chakra 2 などのサポートを含む、主要なライブラリの更新

🔨 オプション: 拡張テンプレート — プロジェクトのコード占有領域を大幅に削減し、開発の手間、所有コスト、そして将来のアップグレードに関する問題を軽減します。詳細については、テンプレートの拡張性のガイドを参照してください。

🪝 オプション: @salesforce/commerce-sdk-react “hooks” の統合 — API 呼び出しをプロジェクトの実装から分離することにより、API 呼び出しを npm ライブラリとの依存関係としてアップグレードできるようにします。これにより、TanStack クエリを介して多くの優れた機能 (状態管理などを含む) がもたらされます。開始するには、Commerce SDK React ドキュメントをご覧ください。

必要な変更 

新しい SDK パッケージ 

PWA Kit SDK は、@salesforce NPM 組織に移動されました。v3 にアップグレードするには、新しいパッケージをインストールし、次のパッケージのすべての import ステートメントを置き換えます。

  • pwa-kit-react-sdk -> @salesforce/pwa-kit-react-sdk@^3
  • pwa-kit-runtime -> @salesforce/pwa-kit-runtime@^3
  • pwa-kit-dev -> @salesforce/pwa-kit-dev@^3
  • pwa-kit-create-app -> @salesforce/pwa-kit-create-app@^3
  • retail-react-app -> @salesforce/retail-react-app@^3

getProps の破壊的変更 

v3.0.0 以降、PWA Kit では withReactQuery という新しいデータフェッチ戦略が導入されています。この戦略は react-query ライブラリを使用し、React フックを記述してデータを同型で取得できるようにします。getProps とは異なり、クライアント側とサーバー側のデータ取得ロジックを複製する必要がなくなりました。このバージョンでは、デフォルトで、@salesforce/retail-react-appreact-query を利用した @salesforce/commerce-sdk-react を使用します。

1// app/components/_app-config/index.jsx
2
3import { withLegacyGetProps } from "@salesforce/pwa-kit-react-sdk/ssr/universal/components/with-legacy-get-props";
4
5const AppConfig = ({ children }) => {
6  return <div>My AppConfig</div>;
7};
8
9export default withLegacyGetProps(AppConfig);
  • withReactQuerywithLegacyGetProps は両方同時に使用できます。
  • getPropsshouldGetProps は Retail React App ページのデフォルトテンプレートから削除されましたが、廃止予定ではありません。これらのメソッドは引き続き長期的にサポートされます。

Note

パッケージの依存関係のアップグレード 

以下に示すように、package.json の依存関係を変更します。peerDependencies から @chakra-ui/system を削除し、dependencies または devDependencies に含めます。

1"@chakra-ui/icons": "^2.0.19",
2"@chakra-ui/react": "^2.6.0",
3"@chakra-ui/skip-nav": "^2.0.15",
4"@chakra-ui/system": "^2.5.6",
5"framer-motion": "^10.12.9",
6"react": "^18.2.0",
7"react-dom": "^18.2.0",
8"react-hook-form": "^7.43.9"

Node 18 と npm 9 をサポートするように package.json のエンジンを更新します。

1"engines": {`
2    "node": "^18.0.0",`
3    "npm": "^8.0.0 || ^9.0.0"`
4}

npm i を使用してプロジェクトの依存関係を再インストールします。

オプション: 拡張テンプレートの使用 

v2.7.x から v3.x に移行する場合、拡張テンプレートを使用するかどうかを選択できます。この機能を利用するには、@salesforce/retail-react-app (または将来的には別の拡張可能なテンプレート) から少なくとも 1 つのファイルをインポートする必要があります。

そこから、npx pwa-kit-create-app@2.x を介して生成した元のプロジェクトから変更したファイルの数を検討します。一部のお客様は大量 (おそらく数百) のファイルを所持していますが、その場合でも、かなりの数が変更されない可能性があります。これらの未変更ファイルは @salesforce/retail-react-app からインポートするのに適した候補ですが、このプロセスを慎重かつ段階的に実行することをお勧めします。@salesforce/retail-react-app の多くのファイルは類似していますが、PWA Kit Retail React App の v2.x の以前の状態から変更されています。特に、@salesforce/commerce-sdk-react の統合 (移行の詳細については後で詳述) により、インポートおよびファイル構造の点で多数のファイルが変更され、commerce-api ディレクトリ全体が Retail React App から削除されました。

拡張テンプレートを利用するためにプロジェクトを移行する場合、PWA Kit SDK のバージョン 2.x と npx pwa-kit-create-app@2.x によって生成されたプロジェクトは @salesforce/commerce-sdk-react に依存していないのに対し、新しいコード @salesforce/retail-react-app@^1.x はこのライブラリを頻繁に使用し、多くのファイルに触れて変更することに注意してください。変更の規模を把握するには、この Github の差分 https://github.com/SalesforceCommerceCloud/pwa-kit/compare/release-2.7.x…release-3.0.x?diff=unified#files_bucketrelease-2.7.xrelease-3.0.x を比較し、@salesforce/commerce-sdk-react を検索して、このインポートを追加するすべての追加内容を確認してください。

Note

アプリがバージョン 2.x (app/commerce-api ディレクトリを含む) とコードを共有しようとすると、そのコードが commerce-api フォルダーと @salesforce/commerce-sdk-react npm モジュールの両方に (2 つの非常に異なる形式で) 存在しているバンドルに、不要なコードを追加するリスクがあります。

Warning

オプション: commerce-sdk-react フックライブラリの使用 

v3.0.0 以降、PWA Kit は withReactQuery という別のフェッチ戦略を使用します。この戦略は、react-query ライブラリを活用し、SSR レンダリングパスでのクエリを有効にします。@salesforce/retail-react-app は、react-query を利用した @salesforce/commerce-sdk-react を使用します。

フックがサーバー側で機能するには、AppConfig コンポーネントを新しい高階コンポーネント withReactQuery でラップする必要があります。

デフォルトのフェッチ戦略としての ReactQuery への移行の一環として、@salesforce/retail-react-app@^1 では、レガシーの getProps() 呼び出しが引き続き機能することを保証するための変更が必要です。

Warning

1// app/components/_app-config/index.jsx
2
3import { CommerceApiProvider } from "@salesforce/commerce-sdk-react";
4import { withReactQuery } from "@salesforce/pwa-kit-react-sdk/ssr/universal/components/with-react-query";
5
6const AppConfig = ({ children }) => {
7  return (
8    <CommerceApiProvider
9      clientId="12345678-1234-1234-1234-123412341234"
10      organizationId="f_ecom_aaaa_001"
11      proxy="localhost:3000/mobify/proxy/api"
12      redirectURI="localhost:3000/callback"
13      siteId="RefArch"
14      shortCode="12345678"
15      locale="en-US"
16      currency="USD"
17    >
18      {children}
19    </CommerceApiProvider>
20  );
21};
22
23export default withReactQuery(AppConfig);

トラブルシューティングと最適化 

上記の依存関係のアップグレードを完了すると、以下のライブラリに関連する問題がプロジェクトで発生する可能性があります。以下のセクションでは、これらの問題を解決するためのソリューションを提案します。プロジェクトの詳細はさまざまであり、提案された解決策は絶対的なルールではなくガイドラインとして扱ってください。

react-hook-form のアップグレード 

react-hook-form の移行については、こちらの react-hook-form 公式ドキュメントをお読みください。PWA プロジェクトには、変更が必要な箇所が 2 つあります。

  1. errors オブジェクトが formState オブジェクトに移動されたので、フォーム errors オブジェクトを、app/components/forms/ のフックの破壊のもう 1 つのレイヤーに移動します。
1// app/components/forms/useAddressFields.jsx
2
3    //before
4    export default function useAddressFields({form: {watch, control, errors}, prefix = ''}) {
5
6    //after
7    export default function useAddressFields({
8        form: {
9            watch,
10            control,
11            formState: {errors}
12        },
13        prefix = ''
14    })
  1. Controller のレンダープロップを field のプロップに移動します。Render のコールバック署名は、field fieldState を含むオブジェクトを返します。
1// app/components/field/index.jsx
2
3    // before
4    <Controller
5        name={name}
6        control={control}
7        rules={rules}
8        defaultValue={defaultValue}
9        render={({onChange, value, ref}) => {
10          // other code
11        }}
12    />
13
14    // after
15    <Controller
16        name={name}
17        control={control}
18        rules={rules}
19        defaultValue={defaultValue}
20        render={({field: {onChange, value, ref}}) => {
21          // other code
22        }}
23    />

React 18 ハイドレーションエラー 

React 18 では、ハイドレーション警告は React 17 のような警告ではなくエラーとして表示されます。ハイドレーションエラーが発生した場合、アプリケーションのビルドを妨げるこれらの潜在的なエラーを抑制するには、一部のコードの更新が必要でした。アプリが同形的にレンダリングできるようにするには、これらのエラーの修正が不可欠です。このエラーは、サーバーまたはクライアント間に不一致がある場合に発生します。コンポーネントまたはページが条件付きでレンダリングされる場合は、クライアント固有のコードをレンダリングする前にハイドレーションが完了していることを確認する必要があります。

プロジェクトでハイドレーションが終了したかどうかを判断するユーティリティ関数を作成しましょう。window.__HYDRATING__ で提供される組み込み変数を pwa-kit-react-sdk で使用できます。

1/**
2 * This utility function determines if the app has finished hydration
3 * @return {boolean}
4 */
5export const isHydrated = () =>
6  typeof window !== "undefined" && !window.HYDRATING;

Chakra 2 

プロジェクトで Retail React App テンプレートとは異なる @chakra-ui/react ライブラリのコンポーネントと API を使用している場合は、公式の Chakra 2 移行ドキュメント をご確認ください。

Important

Chakra 2 をサポートするには、Retail React App テンプレートに基づくプロジェクトの更新がいくつかのファイルに必要です。

Chakra 2 では allowMultipleallowToggle を同時に使用できないため、Accordion コンポーネントの allowToggle を削除します。

Footer コンポーネントでは、@chakra-ui/react から StylesProvider を直接インポートする方法は廃止となっています。代わりに createStylesContext('Footer') を使用して作成する必要があります。

1import { createStylesContext } from "@chakra-ui/react";
2
3const [StylesProvider, useStyles] = createStylesContext("Footer");

@testing-library/react 

React テストライブラリ v14.0.0 では、すべてのユーザーアクションが非同期であり、ユーザーアクションを実行する前に setup を呼び出す必要があるため、アクションを呼び出す前に userEvent を設定し、userEvent を使用する単体テストでアクションを待ちます。

たとえば、次のようになります。

1const user = userEvent.setup()`
2// Import `render`and`screen` from the framework library of your choice.
3// See https://testing-library.com/docs/dom-testing-library/install#wrappers
4render(<MyComponent />)
5
6await user.click(screen.getByRole('button', {name: /click me!/i}))

詳細については、testing-library の userEvent の公式ドキュメントを参照してください。

または、テストコンポーネントをレンダリングする直前にsetup() in many unit tests, you can set up your userEvent ‘を繰り返し呼び出しapp/utils/test-util.js、レンダリング結果とともに返すことを避けるために、テストが setup()を呼び出さずにユーザーアクションを実行できるようにします。

1export const renderWithProvider = (children, options) => {
2   // some code here
3   const user = userEvent.setup()
4   const res = render(children, {
5        wrapper: () => (
6            <TestProvidersWithDataAPI {...options?.wrapperProps} locals={locals}>
7                {children}
8            </TestProvidersWithDataAPI>
9        ),
10        ...options
11    })
12    return {user, ...res}
13}
14
15//usage
16test('some test', async () => {
17   const {user} = renderWithProviders(<MyComponent />
18   const button = screen.getByText(/button/)
19   await user.click(button)
20}

Jest-setup 

Jest-setup には、TextDecoderjest-setup.js. で定義されていないというエラーをスローする可能性のある疑似依存関係があります。jest-setup.js に以下を追加します。

1global.TextDecoder = require("util").TextDecoder;

その他の改善点 

PWA Kit SDK のバージョン 2.x または npx pwa-kit-create-app@2.x によって生成されたプロジェクトから移行する場合、@salesforce/commerce-sdk-react コードは大幅にリファクタリングされ、app/commerce-api/ ディレクトリが削除されます。API リクエストを処理し、SDK として機能するこれらのファイルの代わりに、@salesforce/commerce-sdk-react がその機能に取って代わります。SDK はこのライブラリを頻繁に使用するため、SDK の v3 リリースは @salesforce/retail-react-app@^1.x の最初のリリースと相関関係があります。

@salesforce/commerce-sdk-react を実装すると、Retail React App 内の多くのファイルが変更されます。変更のサイズを把握するには、この GitHub の差分release-2.7.xrelease-3.0.x を比較し、@salesforce/commerce-sdk-react を検索してください。差分で、このインポートを含むすべての追加内容をメモします。