TOP

React/Next.js



はじめに

このページは、SPA(Single Page Application)の続きのページです。
ここではReactとNext.jsにスポットを当てて記載していきます。
一応このページから読み進めても問題ないように構成したつもりですが、先のページを見てからの方が、ReactとNext.jsについて理解が深まるかもしれません。


React

Reactとは、WebサイトやWebアプリのUI部分を開発する際に活用するJavaScriptライブラリです。ReactはMeta (旧Facebook社)が開発し、2013年にオープンソース化されました。
リアクティブ・プログラミングを特徴としています。リアクティブとは、『何かの値が変化するとそれに連動して表示内容が自動で変化する仕組み』のことです。
ReactはJavaScript用のフレームワークと勘違いされることがありますが、実際には上述の通りライブラリとなります。CDN(Contents Delivery Network)経由でhtmlファイル内でReactのライブラリを使用することができます。
以下は実際にReactのライブラリを使用して書いたものです。
↓クリックでcounter増加

wait....


上記のソースコード(抜粋)

react_next.html
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
</head>

<style>
  #root {
    cursor: pointer;
    font-size: 20pt;
    background-color: lightblue;
    color: black;
    padding: 10px 1px 5px 1px;
  }
</style>
<div id="root" onClick="doCount();">wait....</div>
<script>
  let counter = 0;
  let dom = document.querySelector('#root');
  doCount();

  function doCount() {
    counter++
    let element = React.createElement(
      'p', {}, "counter: " + counter
    )
    ReactDOM.render(element, dom)
  }
</script>


上記はCDNを使用していますが、これは主に学習目的または小規模のアプリケーション目的での使用が適しています。
大規模な開発をする場合、Reactで重要な概念として以下3つがあります。


JSX(JavaScript XML)

JSXはJavaScriptの構文拡張で、JavaScriptファイル内にHTMLのようなコードを記述できるようにするものです。Meta(旧Facebook社)によって開発されました。たとえば、次のようなコードを書くことが出来ます。
const heading = <h1>Hello, JSX!</h1>;
このコードはHTMLのようにも見えますが、JavaScriptのコードです。headingという定数を作り、その定数にHTMLの要素(React要素)を代入しています。
JSXを記述する場合、ファイルの拡張子は.jsxとします。これにTypeScriptを適用する場合、拡張子は.tsxとします。
JSXでは中括弧 { } を使うことで、以下のようにReact要素内にJavaScriptの値を埋め込むことができます。

let name = 'React';
const heading = <h1>Hello, {name}</h1>;


コンポーネント

Reactでは、自分が書いたマークアップ、CSS、JavaScript を、アプリのための再利用可能な UI 要素にまとめることができます。これを『コンポーネント』と呼びます。
以下コンポーネントの例です。 CommonFooter という名前のコンポーネントを作っています。

common_footer.tsx
export default function CommonFooter(labelKey: any): JSX.Element {
  return (
    <div>
      <footer>
        ...
      </footer>
    </div>
  );
}

このようにして作成したコンポーネントを組み合わせてシステムを構築していくのがReactの考え方です。



React Hooks

React Hooksはフック(Hook)によって関数コンポーネント内で状態やライフサイクルを扱うための機能です。
公式が提供しているフックは10種類あり、さらにフックを組み合わせてカスタムフックを独自に実装できます。

フック種類用途(概要)フック名
状態のフック
(State Hooks)
内部状態を持ち、その状態の変化に応じて表示を変更できる。useState
useReducer
メモ化のフック
(Performance Hooks)
値や関数を保持し、必要のない子要素のレンダリングや計算を
抑止するために使用し、パフォーマンスの最適化を図る
useCallback
useMemo
副作用のフック
(Effect Hooks)
副作用(コンポーネントの描画とは直接関係ない処理のこと)
のためのフック。副作用にはログの出力やデータの取得などが
あります。
useEffect
useLayoutEffect
Contextのためのフック
(Context Hooks)
親の変数を子コンポーネントで受け取るuseContext
refのフック
(Ref Hooks)
レンダリングに使用しない情報を記録useRef
useImperativeHandle
Other Hooksライブラリ開発時に利用されるHooksuseDebugValue

ReactではこのReact Hooksを利用してリアクティブなシステムを構築していきます。
例えば状態のフックであるuseStateなら以下のようなコーディングをすることでリアクティブにすることができます。

const [状態, 更新関数] = useState(初期状態)

const [count, setCount] = useState(0);
<div>
  <p>Count: {count}</p>
  <button onClick={() => setCount(count + 1)}>+</button>
</div>


Next.js

ここではNext.jsの機能について記載していきます。


レンダリング

Nextで使えるレンダリング手法とその概要はこちらに記載しているので、どのようにしてそれを実現するかを記載します。
Nextでは、所定の関数を使用することでレンダリングモードを制御します。レンダリングモードと対応する関数は以下の通り。

レンダリング手法関数名
SSR(Server-Side Rendering)getServerSideProps
CSR(Client-Side Rendering)-(デフォルトがCSRなので使用する関数は無し)
SSG(Static Site Generation)getStaticProps
ISG(Incremental Static Generation)getStaticPaths
ISR(Incremental Static Regeneration)revalidateを返すgetStaticProps
On Demand ISR
(On Demand Incremental Static Regeneration)
revalidateを返すgetStaticPropsとunstable_revalidateの組み合わせ

各関数の細かい使い方に関してはここでは記載しません(各関数の使い方まで記載するスキルが今の自分にはありません)。
使い方に関しては以下のサイトがわかりやすい気がしたのでリンクを張っておきます。



ファイルベースルーティング

Next.jsの「ファイルベースルーティング」は、pagesディレクトリ内のファイル構成がそのままアプリケーションのページ構成になるという機能です。pages ディレクトリ配下にスクリプトファイルを配置すると、それを使って画面表示を行います。
例えば以下の画像のように配置した場合、 http://[ホスト名]/saga1/ability/ability_detail というurlで ability_detail.tsx の内容が表示されます。



Next.js では、すべてのページの初期化に App というコンポーネントを使用しています。pages/_app.tsx を作成することで、Appコンポーネントをカスタマイズできます。全ページに共通のcssを適応するなど、すべてのページで共通な処理などを書くことができます。


以下のコードは_app.tsxの中身です。1行目でimport '../styles/style.css'を記述することで、pages配下のすべてのページに style.css が適応されます。

_app.tsx
import '../styles/style.css'
function MyApp({ Component, pageProps }) {
  return 
}
export default MyApp


作った物

ゲームのサガシリーズが好きなので、初代サガである魔界塔士サガに関するものを作ってみました。
とりあえず作った後に勉強したので、今見ると作り直したくなる感じはあるが、一旦現時点(2024年5月)のものとして公開してみます。
ソースコードを全量載せると結構な量になるので、だいぶ抜粋して紹介します。

各ページのレイアウトを統一するために、共通部分を CommonBody, CommonHeader, CommonFooter に分けてそれぞれコンポーネント化し、ページ毎に異なる部分を別途作成してそれを埋め込む形で作成しています。



以下はWebサイトに表示するページのフッター部分を CommonFooter という名前でコンポーネント化したものです。

common_footer.tsx
export default function CommonFooter(labelKey: any): JSX.Element {
return (
  <div>
    <footer>
      共通フッターの内容をここに書く
    </footer>
  </div>
);
}

コンポーネント化したフッターを呼び出す際は <CommonFooter /> と記述します。
以下は同じくコンポーネント化した CommonBody から CommonFooter を呼び出しているサンプルです。
また、 CommonBody の呼び出し元から引数で渡された内容をbody部に設定しています({body.message}の部分)。

common_body.tsx
import CommonFooter from "./common_footer";

export default function CommonBody(body: any): JSX.Element {
<body>
  <section className="box">
    {body.message}  // 引数(body)から渡された値(message)を設定
    ...
  </section>
  <CommonFooter />  // CommonFooter コンポーネント呼び出し
</body>
}


共通のコンポーネントはこんな感じで、あとは各ページ個別の内容を pages ディレクトリ配下に配置し、作成したコンポーネントをその中で呼び出して使うようにしていました。
今回は pages 直下に monster_trans_mechanism.tsx というファイル名で配置しているので、サーバ起動後、 http://ホスト名:3000/monster_trans_mechanism でアクセスできます。
以下が monster_trans_mechanism.tsx の中身です。<CommonBody message={this.CreateBody()} />の部分で CommonBody コンポーネントを呼び出し、 message という名前の変数に CreateBody() 関数の戻り値を設定しています。

pages/monster_trans_mechanism.tsx
import CommonBody from '../common/common_body';

export default class MonsterTransform extends Component {
constructor(props: {} | Readonly<{}>) {
  super(props)
}

render() {
  return (
    <>
    // CommonBodyコンポーネント呼び出し。
    // CreateBody()の戻り値を引数(messageという名前の変数)で渡している。
    <CommonBody message={this.CreateBody()} />
    </>
  )
}

// body部の生成
CreateBody() {
  let body =
  <>
    各ページの内容をここに書く
  </>
  return body
}
};


基本的に上記のような構成で全てのページを作成しています。
実際に作った物は大まかに以下。

  • 仲間モンスター一覧
  • 変身後モンスター算出
  • 能力一覧

魔界塔士サガはモンスターを仲間にすることができ、そのモンスターは敵が落とす肉を食べることでより強力なモンスターに変身し、成長するというシステムになっています。何に変身するかは(普通は)分からないようになっています。
仲間になるモンスターを一覧化したものが仲間モンスター一覧で、変身後に何のモンスターになるかを算出してくれるのが変身後モンスター算出ツールです。
※変身後モンスター算出ツールの初期表示が遅いが、おそらく適切なレンダリング手法になっていないからだと思われる。今後改善予定。

変身後モンスター算出ツールは、画面上段のプルダウンを選択すると、それに合わせて画面下の変身後の部分がリアクティブに切り替わるようになっています。
下段の変身後の部分だけがレンダリングされるようになっています。


仲間モンスター一覧の方は、テーブルのヘッダー部分を操作することで表示対象を絞ったり、並び替えができるようになっており、その度に一覧の部分だけが再レンダリングされるようになっています。
また、一覧のモンスター名がリンクになっていて、そこから当該モンスターの詳細情報を確認できる画面に遷移できるようになっています。
このモンスターの詳細情報は、変身後モンスター算出画面でも同じ内容が出力されるようになっています。ソースコードに同じ内容を2回書きたくないので、各モンスターの詳細情報部分をbody部として切り出し、それを詳細画面と変身後モンスター算出の詳細欄の両方で流用する形にしている。

変身後モンスター算出の詳細欄


モンスター一覧の詳細欄。パンくずリスト以外は同じ内容になっていることがわかる。



あと作ったのは、モンスターが使用する能力の詳細。
画面のレイアウトをReactで記載し、表示する情報自体はDBに格納。
URLからIDを取得してそれをもとにDBから情報を取得して画面をレンダリングするようになっています。


リンクは↓こちら。URL の ability_id を変えることで内容が変わることが確認できると思います。
能力詳細


React/Next.jsを使って作ってみたものの紹介は以上です。



参考書籍

参考サイト