Shopify Hydrogen
Hydrogenの思想では、queryはコンポーネントごとに取得できることを目指している。トップレベルですべてのデータを取得して、子コンポーネントにバケツリレーするのではなく、それぞれのcomponent内に直接記述する。
fetchの並列処理を行う箇所はnetwork request waterfallsを避けるためにpreload: true
を設定する。
const {data} = useShopQuery({
query: QUERY,
variables: {
handle: '***',
},
preload: true,
});
Netlifyのデプロイ注意点
RSCが動作するためには、Edge Functionsが必要だが、これはまだベータ版の機能のため、Netlify-sideのaccess control(パスワードロック)を使用するとEdge Functionは適用されなくなる。(ベータ版の制限)
Cloudflare Workersへデプロイする場合。
Cloudflareの場合、環境変数をこのドキュメントに従い追加し、encryptして値は秘匿させておく。もしくはwrangler secret put <KEY>
で変数の登録をする。
後から確認しやすいようにwrangler.tomlに下記のように、必要な変数をコメントで残しておく。
# The necessary environment variables are:
# - SHOPIFY_STORENAME
# - SHOPIFY_STOREFRONT_TOKEN
apiの2022-10ver.からlistタイプのメタフィールドの、references
が取得可能になった。(それ以前はnullしか返ってこない)。
「関連商品」などの登録をメタフィールドでも行いやすくなった。
現在、Internal Server Errorのコンポーネント自体は変更することができない。
そのため自前でError Boundary処理を行い、カスタムErrorページを表示させる。
Error Boundary処理は関数コンポーネントではなくClassコンポーネントを書く必要があるが、そのあたりを処理してくれる便利なパッケージがあるのでそれを使用する。
yarn add react-error-boundary
RSCでは直接は動かせないので、一旦clientコンポーネントを作成し、wrapする必要がある。
エラーのログもconsoleに吐くようにしておく。
import {ErrorBoundary, FallbackProps} from 'react-error-boundary';
export function CustomErrorBoundary({children}: {children: React.ReactNode}) {
return (
<ErrorBoundary FallbackComponent={ErrorFallback} onError={onError}>
{children}
</ErrorBoundary>
);
}
const onError = (error: Error, info: {componentStack: string}) => {
console.log('error.message', error.message);
console.log('info.componentStack:', info.componentStack);
};
function ErrorFallback({resetErrorBoundary}: FallbackProps) {
return (
<div role="alert">
<h1>
表示できません
</h1>
<button type="button" onClick={resetErrorBoundary}>
再試行する
</button>
</div>
);
}
あとはApp.server.tsxで全体をwrapしてやればよい。
function App({request, response}: HydrogenRouteProps) {
return (
<CustomErrorBoundary>
......
ここにカスタムできるserver error pageのやり方があった。
/src/Error.jsx
にファイルを置けばよいらしい。
Cloudflareにデプロイすると文字がたまにバグる。もしかするとマルチバイトの言語に対応してないのかもしれない。
Hydrogenはコンテンツを<meta data-flight>
というchunksに分割しているらしい。その過程で、文字がマルチバイトのため、途中で分割してしまったのではないかと思う。
Hydrogenがdecodeするときに、streamオプションを渡してやると解決するのかもしれない。
// @shopify/hydrogen/dist/esnext/entry-server.js
function tagOnWrite(response) {
......
response.write = (arg) => {
if (arg instanceof Uint8Array) {
savedChunks.push(decoder.decode(arg, stream)); // <--- adding stream option
MarkDownのparserにはreact-remark
はnullが返ってきてしまった。
marked
を代わりに使用した。
import {marked} from 'marked';
export function MarkDownRender({
md,
className = '',
}: {
md: string;
className?: string;
}): JSX.Element | null {
const body = marked(md);
return (
<div
dangerouslySetInnerHTML={{
__html: body,
}}
className={className}
/>
);
}
hydrogenではOxygenを使用しない場合にはRate Limit対策は必須と書かれている。
通常のStorefront APIではPublic Tokenを使用するが、delegate access token
(private token)を使用してStorefront APIを叩くらしい。
delegate access token
はここを参考に、GraphQL AdminかREST Admin APIを使用してmutation
させればトークンが発行される。
セキュリティ面から、delegateAccessScope
は必要最低限のもののみを指定する方がよいそうです。
このトークンはenvに秘匿し、hydrogen.config.ts
のprivateStorefrontToken
で読み込めば良い。
Hydrogenが用意しているSeoコンポーネントでは、type="product"
の場合は画像を動的に設定できるがほかのページでは用意されていない。
カスタムで<head>
内にタグを追加したい場合は、ここに説明があるように<Head>
タグを使用する。
DefaultSeoコンポーネントに<Head>
でデフォルトのOGP画像を設定しておく。type="product"
のページでは自動で上書きされる模様。
import {Head} from '@shopify/hydrogen';
<Head>
<meta property="og:image" content={DEFAULT_OGP_IMAGE} />
</Head>