Next.js とQiita API を使って、記事データを取得する

はじめに

学習したことのアウトプットとして記録しました。 現在プログラミング学習中の実務未経験の者であり、技術的な内容などに誤りを含む可能性があるので不適切な記述などがありましたらコメントで教えていただけると幸いです


今回はuseSWRを使用しました

https://swr.vercel.app/ja/docs/getting-started

  • SWRとは、Next.jsを作成しているVercel製のライブラリ

useSWRは外部APIからのデータ取得、ローディング状態、エラーが発生した時をシンプルに記述できます。

Suspenseを使わず、useEffectで取得したデータをuseStateに格納して利用するコードの場合

const Profile = () => {
  const [profile, setProfile] = useState(null)
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    (async () => {
      setLoading(true)

      const res = await fetch('/api/user')
      setProfile(await res.json())

      setLoading(false)
    })()
  }, [])

  if (profile === null || loading) {
    return <div>loading</div>
  }

  return <div>hello {profile.name}</div>
}

useSWRを使うと短く記述できる

import useSWR from 'swr'

function Profile () {
  const { data, error, isLoading } = useSWR('/api/user/123', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>

  // データをレンダリングする
  return <div>{data.name}</div>
}

レスポンスの型を用意

// Pick で利用したいプロパティのみを抽出する

export type QiitaItemsProps = Pick<
  QiitaItemResponse,
  'id' | 'title' | 'user' | 'tags' | 'url'
>;

// Qiita Api レスポンスの型定義
export interface QiitaItemResponse {
  rendered_body: string;
  body: string;
  coediting: boolean;
  comments_count: number;
  created_at: string;
  group: {
    created_at: string;
    description: string;
    name: string;
    private: boolean;
    updated_at: string;
    url_name: string;
  };
  id: string;
  likes_count: number;
  private: boolean;
  reactions_count: number;
  tags: [
    {
      name: string;
      versions: string[];
    },
  ];
  title: string;
  updated_at: string;
  url: string;
  user: {
    description: string;
    facebook_id: string;
    followees_count: number;
    followers_count: number;
    github_login_name: string;
    id: string;
    items_count: number;
    linkedin_id: string;
    location: string;
    name: string;
    organization: string;
    permanent_id: number;
    profile_image_url: string;
    team_only: boolean;
    twitter_screen_name: string;
    website_url: string;
  };
  page_views_count: number;
  team_membership: {
    name: string;
  };
}

検索キーワードからQiitaAPIを叩いて検索結果を一覧表示する

export default function QiitaArticle() {
    const [query, setQuery] = useState('');
    const apiUrl = '<https://qiita.com/api/v2/items?per_page=25&query=>';
    const { data, error } = useSWR(
    query ? `${apiUrl}${encodeURIComponent(query)}` : null,
    fetcher
  );

検索フォーム

    <form onSubmit={handleFormSubmit} className='mt-12 mb-6'>
      <input
        type='text'
        placeholder='例: React'
        value={query}
        className='shadow appearance-none border rounded w-10/12 py-2 px-3 mb-4 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          setQuery(e.target.value)
        }
      />
      <button
        type='submit'
        className='text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline'
      >
        検索
      </button>
    </form>

<div>
  {error && <div>データの読み込み中にエラーが発生しました。</div>}
  {data && (
    <div>
      {data.map((item: QiitaItemsProps) => (
        <QiitaItem key={item.id} item={item} />
      ))}
    </div>
  )}
</div>