SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Remixを通じてWebを学ぶ

React向けのフレームワーク「Remix」で初めてのWebサイトをつくる

Remixを通じてWebを学ぶ 第2回


  • X ポスト
  • このエントリーをはてなブックマークに追加

Remixプロジェクトの構成

 実際にページの実装に入る前に、簡単にプロジェクト内のファイルの役割を確認していきましょう。Remixフレームワークが扱う主要なファイル・フォルダを図3にまとめました。

図3:Remixプロジェクトの構成
図3:Remixプロジェクトの構成

 app/routes フォルダには、Reactコンポーネントが export default でエクスポートされているファイルを配置します。Remixはファイルシステムベースのルーティング機構(file-system based router)を備えているため、ファイル名がそのまま、 export default されたコンポーネントを表示するためのURLになります。図3では、app/routes/index.jsx が配置されていますが、ここに記述したコンポーネントは、ルートパス( / )にアクセスしたときに表示されることとなります。後述しますが、 app/routes 内にフォルダを作って、その中にコンポーネントのファイルを定義していけば、URLのパスを深くしていくことができます。

 app/entry.client.jsx は、ブラウザ側でのコンポーネント描画である、クライアントサイドレンダリング(CSR)のエントリーポイント(起点)となるファイルです。ブラウザ側でのアプリケーションの実行はここから始まります。Reactに詳しい人は、 ReactDOM.hydrateRoot() を行う場所とご理解ください。

 app/entry.server.jsx は、サーバー側でのコンポーネント描画であり、HTMLの生成処理でもある、サーバーサイドレンダリング(SSR)のエントリーポイントとなるファイルです。サーバー側での処理がここから始まるため、今回紹介するファイル・フォルダの中では一番早いタイミングで実行されることになります。

 app/root.jsx は、HTMLのベースとなるコンポーネントを記述したファイルです。 <html><head><body> に何を埋め込むのかを決定する役割を持つため、ブラウザから見ると最も重要なファイルの一つとなります。SSRもCSRも、このファイルを元にしてレンダリングを行います。

 public フォルダには、ブラウザに配信する静的ファイルが格納されます。画像などに加えて、ビルド済みのJavaScriptファイルが public/build として配信されたりもします。

 最後に、 remix.config.js はRemixの設定ファイルです。Remixのビルド先のフォルダを変更したり、 app/routes のファイルパスとは違ったパスのURLを定義するときにも利用できます。

 基本的には、Remixはこれらのファイルの組み合わせで成立しています。UIライブラリをセットアップする際などは entryroot のファイルを設定することもありますが、多くの場合は app/routes を起点にページ実装を進めていくことになると覚えておくとよいでしょう。

サーバーで取得したデータを画面に表示する

 それでは、実際にhackernews-viewerを編集してみましょう。まずは、通信用の便利関数を用意しておきます(リスト3)。

[リスト3]app/utils/hackerNews.server.js
/** Hacker Newsの人気・最新の記事500件の記事IDを取得する */
export async function getTopStories() {
  return fetch("https://hacker-news.firebaseio.com/v0/topstories.json").then(
    (res) => res.json()
  );
}
/** 記事の詳細データを取得する */
export async function getItem(id) {
  return fetch(https://hacker-news.firebaseio.com/v0/item/${id}.json).then(
    (res) => res.json()
  );
}

 Remixのフレームワーク内では、サーバーサイドでもクライアントサイドでも、特に区別することなくWeb標準のFetch API(主に fetch() 関数のことです)を利用して通信できます。

 データに着目すると、 getTopStories() で取得できるのは記事のIDの配列だけです。そのため、タイトル等を取得するためには、各IDを getItem() に渡して、記事データを入手する必要があります。

 それでは、リスト3の関数を使って、人気・最新の記事20件を表示するUIを作成してみましょう。 app/routes/top20.jsx を作成して、リスト4のコードを記述します。CSSファイルはサンプルコードに入っているので、手元で動かしてみる場合はご利用ください。

[リスト4]app/routes/top20.jsx
import { useLoaderData, Link } from "@remix-run/react";
import { getItem, getTopStories } from '~/utils/hackerNews.server';
import stylesUrl from '~/style/top20.css';

// (5) このページで読み込むCSSファイルを指定する
export const links = () => {
  return [{ rel: "stylesheet", href: stylesUrl }];
};

// (1) サーバーサイドでデータを取得する
export const loader = async () => {
  // 500件のデータを取得する
  const top500Ids = await getTopStories();
  // 上位20件のIDだけに絞り込む
  const top20Ids = top500Ids.slice(0, 20);
  // 上位20件の記事データを取得する
  const top20 = await Promise.all(top20Ids.map((id) => getItem(id)));
  // 記事データのIDとタイトルだけに絞り込む
  const top20Summary = top20.map((item) => ({
    id: item.id,
    title: item.title,
  }));
  // idとtitleのみのオブジェクトが20件入った配列を返す
  return top20Summary;
};

// /top20 のURLで表示するページのコンポーネント
export default function Top20Route() {
  // (2) loaderで取得済みのデータを取り出す
  const data = useLoaderData();

  return (
    <div>
      <header>
        <h1>Hacker News Viewer</h1>
      </header>
      <div id="container">
        <div id="sidebar">
          <h2>Top 20</h2>
          <nav>
            <ul>
              {data.map((item) => (
                <li key={item.id}>
                  {/* (3) タイトルをリンクにする */}
                  <Link to={`/top20/${item.id}`}>{item.title}</Link>
                </li>
              ))}
            </ul>
          </nav>
        </div>
        <main>
          {/* (4) */}
          <div>本文をここに表示する</div>
        </main>
      </div>
    </div>
  );
}

 (1)で定義した loader() 関数は、画面の表示前にサーバーサイドでデータを読み込んでおくために、フレームワークから呼び出されるものです。リスト3の便利関数を使いながら、20件分の記事データの配列を作り出しています。 loader() で作成したデータは、コンポーネントを描画し始めたタイミングで呼び出された(2)の useLoaderData() で取り出すことができ、画面の描画に利用できます。記事データの配列は、リストとして表示し、(3)でリンクにしました。後ほど、このリンク先のページも作成します。リンクを作成するために使用した <Link> は、Remixでシングルページアプリケーションとしての画面遷移を行うためのコンポーネントです。リンクをクリックしたら、(4)の領域に本文を表示する予定ですが、少し特殊な仕組みを使う予定なので、今のところは仮置きのテキストを表示するだけにしておきます。

 余談ですが、リスト4ではアクセシビリティに配慮したマークアップを心がけてみました。(3)は記事一覧のメニューとして扱いたいので <nav> 要素で囲み、(4)は本文なので <main> 要素で囲んであります。明確に役割があるものについては、 <div> 要素ではなく、こういった要素を使うことも意識するとよいでしょう。HTMLの文書構造をきれいにした後は、(5)の links にCSSを登録することで、 <head> 要素内に <link> 要素を生成し、スタイルを有効にして見た目を整えています。

 では、実際に動かしてみましょう。ターミナルで npm run dev を実行して、 http://localhost:3000/top20 にアクセスします(図4)。

図4:タイトルの一覧を表示した様子
図4:タイトルの一覧を表示した様子

 記事のリストをメニューとして表示できました。ちゃんと20件のリンクが並んでいますね。リンクをクリックしてみると、 404 Not Found が表示されますが、まだ該当のページを作成していないので、期待通りの挙動です。

まとめ

 今回はRemixのプロジェクト構成を解説し、データ読み込みの方法を簡単に解説しました。次回は <main> の中身だけを切り替えられるNested Routes機能の解説を中心に、Remixの基本を解説します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Remixを通じてWebを学ぶ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/17097 2023/01/14 20:37

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング