[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 = () => {