WordPressの一部にReactを取り入れる

## 目標
本記事はReactでアプリを作成することが目的ではなく、Reactで作成したものをWordPress内に取り入れることが目標となっております。
そのため、Reactのコンポーネント化等、最適化等はしておりません。
（TypeScriptも導入しておりません）


## viteを使用して新規Reactアプリを作成する

### viteを使用して新規Reactアプリの作成

```
yarn create vite
```

新規アプリ作成方法が分からない方は以下参考にしてみて下さい。
https://ja.vitejs.dev/guide/

https://qiita.com/teradonburi/items/fcdd900adb069811bfda

（今回はTypeScript選択せずに、 JavaScriptを選択しました）


![スクリーンショット 2024-01-21 15.10.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/23656b0c-a0aa-dd55-2667-48b3084ac65a.png)

上記コマンド実行してアプリを起動してみます。

以下のような画面が表示できたら成功です！

![スクリーンショット 2024-01-21 15.11.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/9f3bc9b4-82a6-b98e-ca08-f2b8cef44493.png)


### 作成したReactアプリを不要なデータ削除し何もない状態にする

src/App.jsx
の内容を綺麗に削除します。
一旦テストと表示させておきます。

![スクリーンショット 2024-01-21 20.27.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/a583a82f-28ef-5c70-7d5c-311b7c8bb5dc.png)


![スクリーンショット 2024-01-21 15.30.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/9b381a1a-23a5-a04d-ee57-0e2ba287fc03.png)


### WordPressの記事をJSON表示させてみる

実はWordPressは何もしなくてもJSONデータを生成して表示しています。。

```
https://{ドメイン}/wp-json/wp/v2/{表示したい記事種類等}
```
とすると確認できます。

実際に私のブログサイトで試してみます。

```
https://just-a-mother.com/wp-json/wp/v2/posts
```
私のブログは全て既存の投稿を使用しているの記事の種類はpostです。
ここがカスタム投稿タイプや固定ページなどになると変わってきます。

詳しくはいろいろあるので、自分が取得したい記事内容など調べてみて下さい。


上記にアクセスすると以下のようなJSONデータを確認することができます。

![スクリーンショット 2024-01-21 15.21.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/c1038c6a-2a9c-bbac-3687-70037c7d5840.png)

上記のように成形されずただの文字列っぽく見えてしまう人はChromeの拡張機能の「JSON Viewer」

https://chromewebstore.google.com/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh?hl=ja&pli=1

などを入れたら見やすくなります！


### WordPressの記事をReatで取得してみる

apiを使用してHTTPリクエストするために今回はａｘｉｏｓをインストールして使用します。

```
yarn add axios
```
axios以外にもいろいろあるので、良ければ調べてみて下さい。


```react
import { useEffect, useState } from "react";
import "./App.css";
import axios from "axios";

function App() {
  const [posts, setPosts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const URL = "https://just-a-mother.com/wp-json/wp/v2/posts";
    axios.get(URL).then((res) => {
      setPosts(res.data);
      setIsLoading(false);
    });
  }, []);
  console.log("posts", posts);
  return (
    <>
      <div>テスト</div>
    </>
  );
}

export default App;

```

上記のように最低限の記述ですが、コンソールログに取得した記事を出してみます。
ちゃんと１０件分の記事が取得できました

![スクリーンショット 2024-01-21 15.37.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/4380d7cc-312a-ac58-2fe0-4de285f996ef.png)

ですが、10件しか取得できていないですね。。
調べたところ、デフォルトで10件、パラメータ付きで１00件までとありましたので、

```react

const URL = "https://just-a-mother.com/wp-json/wp/v2/posts";
↓
const URL = "https://just-a-mother.com/wp-json/wp/v2/posts?per_page=100";

```
としたところ、無事に全件取得できました！
（私のブログには２８件しか記事がないんで、、、）

![スクリーンショット 2024-01-21 15.51.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/a7799694-25c8-3e75-9de2-8e4246923e7d.png)

もう一つ、今回は記事に設定してあるタグ情報を取得します。
下記でWordPressの記事投稿で設定しているタグ情報を取得できました。

```react

const TAGS_URL = "https://just-a-mother.com/wp-json/wp/v2/tags";
 axios.get(TAGS_URL).then((res) => {
      setTags(res.data);
    });

```

こんな感じで取得できています！

![スクリーンショット 2024-01-21 19.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/21a99021-9f44-ac8b-57a6-496593f95cb0.png)

上記getするコードを追記しても良いのですが、
二つのapiでまとめてgetしたいのでPromise.allに記述を変更します。

```react

axios.get(POSTS_URL).then((res) => {
      setPosts(res.data);
    });

axios.get(TAGS_URL).then((res) => {
      setTags(res.data);
      setIsLoading(false);
    });

// 以下に書き換える　↓

useEffect(() => {
    Promise.all([axios.get(POSTS_URL), axios.get(TAGS_URL)]).then(
      ([postsResponse, tagsResponse]) => {
        setPosts(postsResponse.data);
        setTags(tagsResponse.data);
        setIsLoading(false);
      }
    );

```

まず、タグリストを表示させます。

下記でタグリストが表示されました！
（CSSは当ててます）


```react
 {isLoading ? null : (
        <div className="">
           <ul className="tag-list">
            {tags.map((tag, i) => (
              <li key={i} className="tag-item">
                {tag.name}
              </li>
            ))}
            </ul>
        </div>
      )}

```

![スクリーンショット 2024-01-21 20.44.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/85e7dd9a-4573-917f-060e-15e8207405f2.png)


### クリックしたタグが付与されている記事を下へ表示させる

次にタグボタン下に選択したタグに関連する記事を表示させてみます。

```react

 const handleFiler = useCallback(
    (id) => {
      setFilteredPosts(posts.filter((post) => post.tags.includes(id)));
    },
    [posts]
  );
  
```

上記フィルターする関数を作成します。
ulの方も以下のように修正します。

```react
<ul className="tag-list">
    {tags.map((tag, i) => (
        <li
            key={i}
            className="tag-item"
            onClick={() => handleFiler(tag.id)} // clickしてidが取得できるように追記
        >
            {tag.name}
        </li>
    ))}
</ul>
<ul className="post-list">
    {filteredPosts.map((post, i) => (
        <li key={i} className="post-item">
            <a href={post.link} className="post-link">
                {post.title.rendered}
            </a>
        </li>
    ))}
</ul>

```

これでクリックしたら該当タグが付与されてた記事が下へ表示できるようになりました。

![スクリーンショット 2024-01-21 21.35.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/7950b7b1-c817-d52e-6692-df4001125f7f.png)



### Reactアプリをビルドする
上記までで簡単ではありますが、Reactを使用したアプリが完成できました。

ビルドします。
ビルドするとdistディレクトリができました。

![スクリーンショット 2024-01-22 20.48.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/c6dc5d4f-c69c-bfa3-f940-2ed9ae3edb29.png)

dist/assets/index--1kmMQDK.css
dist/assets/index-Zr6b2y_5.js
この二つのソースを使用します！


## WordPressに組み込んでみる
先ほどビルドしたソースを使用します。

### オリジナルテーマに組み込む
例えば特定の固定ページに組み込みたいとします。

```php
page-{記事slug}.php

<!-- 記事ページ -->
<?php get_header(); ?>
<section class="main_post">
    <div id="app"></div>  //ここにReact組み込む
    <!-- サイトバー -->
    <?php get_sidebar(); ?>
</section>
<?php get_footer(); ?>
```

記事のPHPテンプレートの組み込みたい場所に

```
<div id="app"></div>
```

を入れます。
そして、このページに先ほどビルドしたjsファイルとCSSファイルを読み込みします。
もちろん、サーバーへjsファイル、cssファイルをアップすることもお忘れなく！


### 有料テーマを使用しつつ、特定ページに組み込む

私はブログ用にSWELL（有料テーマ）を使用しています。
この特定の固定ページに組み込みたいと思います。

カスタムHTMLを入力できる枠を出し、そこに
```
<div id="app"></div>
```
を記載します。

![スクリーンショット 2024-01-21 22.00.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/81f792de-742b-6bc7-a0e3-0deb1c25adb0.png)


その下のこのページに読み込むCSSとJSという箇所に先ほどビルドしたファイルをコピぺします。

![スクリーンショット 2024-01-21 21.59.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/c5931b3f-448b-18b3-95df-d2c16320b1c7.png)


そして更新＆公開


![スクリーンショット 2024-01-21 22.01.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/14608d1c-dfe3-2886-42d7-56cfadf9cef4.png)


ちゃんと表示できました！


### なぜid="app"？？
Reactアプリを組み込みしたい箇所には


```
<div id="app"></div>
```

としたらそこにReactで作成したアプリが表示されました。
なぜid="app"なのか？他のidだとダメ？？

Reactアプリを生成する際の大元の
index.htmlにid="app"としています。

![スクリーンショット 2024-01-22 20.55.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/e2ad6653-191f-860b-8e32-b7f3ed1ca6d4.png)

そして、そこに
src/main.jsxにて

```js
document.getElementById("app")

```
とid="app"に<App />をレンダーしているのでid="app"にしないといけません。

![スクリーンショット 2024-01-22 20.56.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483702/2709c6ec-a3f6-9549-62ba-a2236fd24950.png)


ここのidを例えばid="myApp"とかにしていれば、id="myApp"を使用しないといけません。
なので、どんな時でもid="app"ではないということは気をつけましょう。



https://just-a-mother.com/sample/react-api/

上記が実際に作成したReactアプリを組み込みしたページとなります。



##　補足
最終的にapp.jsxのソースはこちらです。

```jsx
import { useEffect, useState, useMemo, useCallback } from "react";
import "./App.css";
import "./reset.css";
import axios from "axios";

const POSTS_URL = "https://just-a-mother.com/wp-json/wp/v2/posts?per_page=100";
const TAGS_URL = "https://just-a-mother.com/wp-json/wp/v2/tags";

function App() {
  const [posts, setPosts] = useState([]);
  const [tags, setTags] = useState([]);
  const [filteredPosts, setFilteredPosts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    Promise.all([axios.get(POSTS_URL), axios.get(TAGS_URL)]).then(
      ([postsResponse, tagsResponse]) => {
        setPosts(postsResponse.data);
        setTags(tagsResponse.data);
        setIsLoading(false);
      }
    );
  }, []);

  const handleFiler = useCallback(
    (id) => {
      console.log("id", id);
      setFilteredPosts(posts.filter((post) => post.tags.includes(id)));
    },
    [posts]
  );

  return (
    <>
      {isLoading ? null : (
        <div className="">
          <h2 className="title">興味がある分野を選んで下さい</h2>
          <ul className="tag-list">
            {tags.map((tag, i) => (
              <li
                key={i}
                className="tag-item"
                onClick={() => handleFiler(tag.id)}
              >
                {tag.name}
              </li>
            ))}
          </ul>
          <ul className="post-list">
            {filteredPosts.map((post, i) => (
              <li key={i} className="post-item">
                <a href={post.link} className="post-link">
                  {post.title.rendered}
                </a>
              </li>
            ))}
          </ul>
        </div>
      )}
    </>
  );
}

export default App;

```

以上です。
WordPressや静的サイトに気軽にReactアプリを組み込みして見て下さいね！