【React】HTMLにscriptタグでjsonデータを埋め込んでそれを取得して使う方法
HTMLにscriptタグでjsonデータを埋め込んでそれを取得して使う方法を紹介します。
apiを叩いて取得するのではなく、phpなどであらかじめjsonデータをHTMLに落としてそれをJavaScriptで取得して使う方法を紹介します。
以下はReactの例ですが、別にReactでなくても利用できます。
React v19の最新APIであるuseフックとServer Componentsを使った実例を詳しく解紹介しています。
サーバーサイドがPHPやrubyなどの言語の場合
サーバーがPHPやrubyなどの言語の場合は、以下のように役割分担します。
- PHP: サーバーサイドでJSONデータを生成してHTMLに埋め込む。
- React: クライアントサイドで埋め込まれたJSONデータを取得し、表示や処理を行う。
シンプルなサイトや、すでにPHPで構築されたシステムがある場合や、データ取得にサーバーレンダリングを必要としない場合におすすめです。
サーバーサイドがNode.jsの場合
React v19での最新のAPIであるuseフックとServer Componentsを使えば、非同期データを効率的に扱うことが可能です。
動的で高速なWebアプリが必要な場合や、ReactのServer Componentsや新しいエコシステムを活用したい場合におすすめです。
htmlにscriptタグでjsonデータを埋め込んでそれを取得して使いたいとき
HTMLにscriptをタグを準備する
<!-- JSONデータを埋め込む -->
<script type="application/json" id="catalog-data">
[
{
"categoryId": 1,
"categoryName": "総合カタログ",
"items": [
{
"id": 1,
"title": "総合カタログ",
"imgSrc": "/assets/images/catalog/img01.jpg",
"text": "カタログの内容を、説明する内容が入ります。"
}
]
},
...
]
</script>
上のように、type="application/json"
をscriptタグにつけるのがポイントです。
これでjson形式で埋め込むことが可能になります。
Reactで取得(JavaScriptであればなんでも可)
import React, { useEffect, useState } from 'react';
import SearchForm from './SearchForm';
import CatalogCategory from './CatalogCategory';
import useCatalogData from './helpers/useCatalogData';
// カタログ一覧のコンポーネント
function Catalog({ cartItems, onAddToCart }) {
const catalogData = useCatalogData();
useEffect(() => {
// カタログリンククリック時のイベントハンドラー
const handleCatalogLinkClick = event => {
event.preventDefault();
const catalogId = event.currentTarget.getAttribute('data-catalog-id');
// セッションストレージからカタログIDの配列を取得。存在しない場合は新しい空の配列を作成
const catalogIds = JSON.parse(sessionStorage.getItem('catalogIds')) || [];
// catalogId を数値に変換して一意か調べる
const numericCatalogId = parseInt(catalogId, 10);
if (!catalogIds.some(id => parseInt(id, 10) === numericCatalogId)) {
// カートアイテムを更新
onAddToCart(numericCatalogId);
// catalogId を追加
catalogIds.push(numericCatalogId);
// カタログID一覧をセッションストレージに保存
sessionStorage.setItem('catalogIds', JSON.stringify(catalogIds));
}
};
// カタログリンク要素を取得
const catalogLinks = document.querySelectorAll('.js-catalog-link');
// カタログリンククリック時のイベントリスナーを設定
catalogLinks.forEach(catalogLink => {
catalogLink.addEventListener('click', handleCatalogLinkClick);
});
// コンポーネントがアンマウントされる際にイベントリスナーをクリーンアップ
return () => {
catalogLinks.forEach(catalogLink => {
catalogLink.removeEventListener('click', handleCatalogLinkClick);
});
};
}, []);
return (
<div className="contents__main">
...
</div>
);
}
export default Catalog;
こちらの例では、カタログの一覧データを取得したいときに、HTMLの中にそのデータをjson形式で落とします。
そして、それを取得するためのコードです。
helpers/useCatalogData.js
/**
* カタログデータを取得するカスタムフック
*
* @returns {Object} カタログデータのオブジェクト
*/
function useCatalogData() {
const catalogDataJSON = document.getElementById('catalog-data').textContent;
const catalogData = JSON.parse(catalogDataJSON);
return catalogData;
}
export default useCatalogData;
そして、helpersなどのディレクトリにデータを取得するカスタムフックを作成すればOKです。
React v19のStrict Modeによる再レンダリングの影響
React v19ではStrict Modeがデフォルトで強化され、一部の副作用が2回実行されることがあります。useCatalogDataカスタムフックを使う際、DOMからデータを取得する操作(document.getElementById)が重複実行される可能性があります。この影響を受けないように、一度だけ取得するように修正する必要があります。
修正例:
useCatalogDataフックでuseRefを利用し、JSONデータを一度だけパースします。
import { useRef } from 'react';
function useCatalogData() {
const catalogDataRef = useRef(null);
if (!catalogDataRef.current) {
const catalogDataJSON = document.getElementById('catalog-data').textContent;
catalogDataRef.current = JSON.parse(catalogDataJSON);
}
return catalogDataRef.current;
}
export default useCatalogData;
メタタグの活用
JSONデータをscriptタグではなく、metaタグを使って埋め込む方法も検討できます。これは、SEOへの影響を抑えつつ、JSONデータをHTML内に埋め込むもう一つの方法です。
メタタグ例:
<meta id="catalog-data" name="catalog-data" content='[{"categoryId":1,"categoryName":"総合カタログ","items":[{"id":1,"title":"総合カタログ","imgSrc":"/assets/images/catalog/img01.jpg","text":"カタログの内容を、説明する内容が入ります。"}]}]'>
メタタグから取得するカスタムフック:
function useCatalogData() {
const catalogDataJSON = document.querySelector('meta[name="catalog-data"]').getAttribute('content');
return JSON.parse(catalogDataJSON);
}
export default useCatalogData;
React v19の最新API: Server Components と use フックの概要
Server Components: サーバーサイドで実行されるReactコンポーネント。クライアントに送信されるHTMLを効率的に生成します。
主に重い処理や非同期データ取得に向いています。
クライアントのバンドルサイズを削減します。
use フック: React v19で導入された非同期データを直接処理する新しいフックです。
サーバーサイドやクライアントサイドでPromiseを簡単に扱えます。
データのローディング状態を明示的に管理する必要がありません。
デモアプリ: カタログデータを非同期で取得して表示する
サーバーサイドで非同期データを取得する例
まず、useフックを利用して、サーバーサイドでカタログデータを取得し、それをクライアントに渡す方法を解説します。
Server Components
// src/components/CatalogList.server.js
import { use } from "react";
// 非同期データを取得する関数
async function fetchCatalogData() {
// サーバー上でデータを取得 (例: API やデータベースから取得)
const response = await fetch("https://api.example.com/catalog");
if (!response.ok) {
throw new Error("Failed to fetch catalog data");
}
return response.json();
}
// Server Component
export default function CatalogList() {
// `use` を利用して非同期データを取得
const catalogData = use(fetchCatalogData());
return (
<ul>
{catalogData.map((category) => (
<li key={category.categoryId}>
<h3>{category.categoryName}</h3>
<ul>
{category.items.map((item) => (
<li key={item.id}>
<img src={item.imgSrc} alt={item.title} />
<p>{item.text}</p>
</li>
))}
</ul>
</li>
))}
</ul>
);
}
ルート設定 (App.server.js)
// src/App.server.js
import CatalogList from "./components/CatalogList.server";
export default function App() {
return (
<main>
<h1>Catalog</h1>
<CatalogList />
</main>
);
}
クライアントサイドの組み込み
// src/index.client.js
import { hydrateRoot } from "react-dom";
import App from "./App.server";
// サーバーレンダリングされたAppをクライアントでハイドレート
hydrateRoot(document.getElementById("root"), );
サーバーコンポーネントなしでクライアントでの非同期処理をする場合
クライアントサイドで同じような非同期処理をするにはuseEffectを使いますが、useフックを使うとコードがさらに簡素化できます。
クライアントコンポーネント例
// src/components/CatalogList.client.js
import { use } from "react";
// 非同期データ取得関数
async function fetchCatalogData() {
const response = await fetch("https://api.example.com/catalog");
if (!response.ok) {
throw new Error("Failed to fetch catalog data");
}
return response.json();
}
export default function CatalogList() {
const catalogData = use(fetchCatalogData());
return (
<ul>
{catalogData.map((category) => (
<li key={category.categoryId}>
<h3>{category.categoryName}</h3>
<ul>
{category.items.map((item) => (
<li key={item.id}>
<img src={item.imgSrc} alt={item.title} />
<p>{item.text}</p>
</li>
))}
</ul>
</li>
))}
</ul> );
}
useの使用条件
useフックは非同期データを取得する際、Promiseを直接扱うため、簡素なコードが書けます。
useはReact v19以上でしか利用できません。
Server Componentsの強み
サーバーサイドでHTMLを生成するため、初期表示が高速で、SEOにも適しています。
クライアントとサーバーの分離
Server Componentsは、サーバーでのみ実行されるロジック(データベースアクセスなど)を切り離して記述できます。