[WordPress][TypeScript]RestAPI #1

functions.php
cf_cat_image:画像タイプのカスタムフィールド
cf_cat_disabled:booleanタイプのカスタムフィールド
/wp-json/wp/v2/posts/のデータにカテゴリの詳細情報を追加する

function add_category_details_to_posts($data, $post, $context) {
    $categories = get_the_category($post->ID);
    $category_details = [];

    foreach ($categories as $category) {
        $category_meta = get_term_meta($category->term_id); // カスタムフィールド取得
        $image_id = get_term_meta($category->term_id, 'cf_cat_image', true);
        $category_details[] = [
            'id' => $category->term_id,
            'name' => $category->name,
            'slug' => $category->slug,
            'cf_cat_image' => empty($image_id) ? '' : wp_get_attachment_url($image_id),
            'cf_cat_disabled' => get_term_meta($category->term_id, 'cf_cat_disabled', true),
        ];
    }
    
    $data->data['category_details'] = $category_details;
    return $data;
}
add_filter('rest_prepare_post', 'add_category_details_to_posts', 10, 3);

 
WordPressPosts.tsx

import { useEffect, useState } from 'react';

// 投稿データの型定義
interface CategoryDetail {
  id: number;
  name: string;
  slug: string;
  cf_cat_image: string;
  cf_cat_disabled: "0" | "1"; // 無効フラグが "0" または "1" の文字列
}
interface Post {
  id: number;
  date: string;
  title: { rendered: string };
  content: { rendered: string };
  excerpt: { rendered: string };
  link: string;
  category_details : CategoryDetail[];
}

const WordPressPosts = () => {
  const [posts, setPosts] = useState<Post[]>([]); // 投稿データ
  const [loading, setLoading] = useState<boolean>(true); // ローディング状態
  const [error, setError] = useState<string | null>(null); // エラーメッセージ

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        const response = await fetch('/wp-json/wp/v2/posts/');
        if (!response.ok) {
          throw new Error('投稿データの取得に失敗しました');
        }
        const data: Post[] = await response.json();
        setPosts(data); // データをステートに保存
      } catch (error) {
        setError(error instanceof Error ? error.message : '不明なエラー');
      } finally {
        setLoading(false); // ローディング終了
      }
    };

    fetchPosts();
  }, []); // 初回レンダリング時のみ実行

  // ローディング中の表示
  if (loading) {
    return <div>Loading...</div>;
  }

  // エラー発生時の表示
  if (error) {
    return <div>Error: {error}</div>;
  }

  return (
    <>
    {posts.map(post => (
      <article className="flex">
        <div className="w-7">
          {post.category_details.map(category_detail => (
            <a className="block" href={"/?c="+category_detail.id}><img src={category_detail.cf_cat_image} /></a>
          ))}
        </div>
        <div className="flex-1">
          <h2 className="entry-title">
            <a href={post.link}>{post.title.rendered}</a>
          </h2>
          <div className="relative">
            <ul className="flex">
              {post.category_details.map(category_detail => (
                <li className=""><a href={"/?c="+category_detail.id}>{category_detail.name}</a></li>
              ))}
            </ul>
            <time className="absolute top-0 right-0" dateTime="">{new Date(post.date).toLocaleDateString("ja-JP", {year: "numeric",month: "2-digit",day: "2-digit"}).replace(/\//g, '-')}</time>
          </div>
          <div dangerouslySetInnerHTML={{ __html: post.content.rendered }} />
        </div>
      </article>
    ))}
    </>
  );
};

export default WordPressPosts;

 
main.tsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import WordPressPosts from './WordPressPosts.tsx'

createRoot(document.getElementById('main')!).render(
  <StrictMode>
    <WordPressPosts />
  </StrictMode>,
)

 
下記のようなReact.FC使用は最近は非推奨

const WordPressPosts: React.FC = () => {