Skip to main content

条件付きレンダリングの技法:ReactとNext.js開発者向けのヒントとコツ

著者

Kumar Harsh

blog feature open source security

2023年10月30日

0 分で読めます

条件付きレンダリングとは、ユーザーの認証状態や権限、アプリケーションの状態などの条件に基づいて、複数の候補から特定のコンポーネントだけをレンダリングするWebアプリケーションの手法です。また、クライアントサイドルーティングや遅延読み込みなど、Reactの基本的なUI機能を幅広く実装する際にも利用できます。

この記事では、条件付きレンダリングのメリットや条件付きルーティングとの違い、React、Next.js、Remixでそれぞれを実装する方法を解説します。

Reactにおける条件付きレンダリングのメリット

ReactやReact系フレームワークで構築されたアプリで条件付きレンダリングを活用すると、読み込み時間の短縮など、さまざまなメリットが得られます。

Webアプリケーションの読み込み時間は、読み込むページのDocument Object Model(DOM)のサイズに左右されます。特に、ユーザーがスクロールしないと表示されない要素を含め、多くの要素を保持していると、DOMサイズが不必要に大きくなり、アプリケーションのパフォーマンスが低下することがあります。

このような場合は、必要になるまでリソースの読み込みを遅らせる一般的な手法である遅延読み込みを実装し、ユーザーがスクロールして要素がビューポートに入るまで、コンポーネントのレンダリングを遅らせることができます。Reactで特に広く使われている遅延読み込みライブラリの1つ、react-lazyloadは条件付きレンダリングを利用し、コンポーネントがブラウザーのビューポートに入ったとき、つまりユーザーに見える状態になったときにのみレンダリングします。

パフォーマンスの向上に加え、条件付きレンダリングはユーザーエクスペリエンスの改善にも役立ちます。認証状態(つまり未認証ユーザーにはログインボタン、認証済みユーザーにはログアウトボタンを同じ場所に表示する)やアクセス権限(つまりツイートなどのコンテンツの所有者には編集コントロールや詳細な分析情報を表示し、所有者以外には限られた情報を表示する)など、ユーザーの属性に応じてUIをカスタマイズできます。

さらに、条件付きレンダリングを使えば、データ取得やバックエンドとの通信などのクライアントサイド処理をスムーズに扱えます。処理中にローディングバーを表示したり、リモートソースからデータを読み込んでいる間は空のリストを非表示にしたりできます。

条件付きレンダリングは、react-routerパッケージなどを使った、シングルページReactアプリのクライアントサイドルーティングも可能にします。

つまり、条件付きレンダリングはReactで使われる最も重要な手法の1つと言えるでしょう。

条件付きレンダリングと条件付きルーティングの違い

条件付きレンダリングについて話すと、条件付きルーティングもよく話題になります。条件付きルーティングは、ユーザーやアプリケーションの状態などの条件に基づいてユーザーに表示する内容を制御する点で、条件付きレンダリングと似ています。ただし、コンポーネントをレンダリングまたは破棄するのではなく、ユーザーをあるルート(またはページ)から別のルートへ移動させます。これはアクセス制御の観点から役立ちます。

WebアプリにはURLから直接アクセスできます。つまり、アプリ内のページのURLは、そのページが公開されているかどうかにかかわらず、ユーザーに取得・保存される可能性があります。特定のロールを持つユーザーだけがアクセスできる個人プロフィールや設定ページなど、権限が必要なページのURLが悪用され、制限されたコンテンツへのアクセスが試みられる恐れがあります。

条件付きルーティングを使うと、Reactアプリはページのレンダリングを要求するリクエストに対して、要求元にそのルートへのアクセス権限があるかどうかを確認できます。権限がなければレンダリングを拒否し、別のページにリダイレクトします。リダイレクト先のページでは、認証を求めたり、アクセスが拒否されたことをユーザーに知らせたりするのが一般的です。

たとえば、新しいシークレットウィンドウでhttps://mail.google.com/mailにアクセスして、条件付きルーティングがどのように機能するかを確かめてみましょう。Googleのログインページにリダイレクトされます。これが、裏側で条件付きルーティングが機能している例です。

ただし、SPAの条件付きルーティングはセキュリティを保証するものではありません。Reactアプリ(シングルページ)が未認証ユーザーをリダイレクトする前でも、権限が必要なページのコンテンツはJavaScriptソースコードに含まれています。そのため、権限のないユーザーがデータにアクセスする事態につながる可能性があります。したがって、Next.jsやRemixなどのフレームワークを使い、認証に関連する条件付きルーティングはサーバーサイドで設定するのが最善です。

Reactで条件付きレンダリングを実装する

条件付きレンダリングについて理解したところで、次のセクションではプログラミングの基本概念の1つであるif…else文を使い、Reactアプリに実装する方法を説明します。

まず、次のコマンドを実行して新しいReactアプリを作成します。

npx create-react-app conditional-rendering-react
cd conditional-rendering-react
npm install

アプリの準備ができたら、次のコードをsrc/App.jsファイルに貼り付けます。

import { useState } from "react"

// Define two components
const LoginButton = (props) => {
  return <>
  <div>You are logged out</div>
  <div><button onClick={props.toggleLoggedIn}>Log In</button></div>
  </>
}

const LogoutButton = (props) => {
  return <>
  <div>You are now logged in</div>
  <div><button onClick={props.toggleLoggedIn}>Log Out</button></div>
  </>
}

function App() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use an if...else statement to render one out of two components based on the login state
  if (loggedIn)
    return <LogoutButton toggleLoggedIn={toggleLoggedIn} />
  else
    return <LoginButton toggleLoggedIn={toggleLoggedIn} />
}

export default App;

このコードには、実装方法を説明するインラインコメントが含まれています。

次に、以下のコマンドを実行してアプリを起動します。

npm start

アプリのURL(つまりhttp://localhost:3000)にアクセスすると、画面にテキストとボタンが表示されます。

「ログアウトしています」と表示されたブラウザページと、「ログイン」ボタン。

Log Inボタンをクリックして状態を変更し、再レンダリングを実行してみましょう。ログイン済みユーザー向けのテキストとボタンに切り替わることがわかります。

localhost:3000のブラウザページ。「ログインしました」というメッセージと「ログアウト」ボタンが表示されている

以上です。Reactでの条件付きレンダリングは簡単に実装できます。次は、Next.jsアプリでの方法を見ていきましょう。

Next.jsで条件付きレンダリングと条件付きルーティングを実装する

Next.jsは、意見の定まった本番環境向けのオープンソースReactベースフレームワークで、高いパフォーマンスを発揮するWebアプリの構築を支援します。2016年にVercelがリリースし、以来、Web開発業界を代表するReactベースのフレームワークの1つに成長しました。

Next.jsは標準でサーバーサイドレンダリングと静的レンダリングに対応し、データ取得、ルーティング、サーバーランタイムなど、開発でよくある課題を解決する使いやすい機能を提供します。

Next.jsで条件付きレンダリングを実装する方法

Next.jsとReactではJSXの構文が共通しているため、Reactと同じ条件付きレンダリングの手法をNext.jsにも適用できます。

Next.jsを始めるには、次のコマンドを実行して新しいNext.jsアプリを作成します。

npx create-next-app conditionals-next

画面に表示される指示に従ってアプリを作成します。デフォルトの選択肢を選べますが、TypeScriptについてはNoを選択してください。プロジェクトを作成したら、次のコードをpages/index.jsファイルに貼り付けます。

import { useState } from "react";

// Define two components
const LoginButton = (props) => {
  return <>
    <div>You are logged out</div>
    <div><button onClick={props.toggleLoggedIn}>Log In</button></div>
  </>
}

const LogoutButton = (props) => {
  return <>
    <div>You are now logged in</div>
    <div><button onClick={props.toggleLoggedIn}>Log Out</button></div>
  </>
}

export default function Home() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use the ternary conditional operator to render one out of two components based on login state
  return loggedIn 
  ? <LogoutButton toggleLoggedIn={toggleLoggedIn} /> 
  : <LoginButton toggleLoggedIn={toggleLoggedIn} />
}

このコードには、各ステップで何が行われるかを説明するインラインコメントが含まれています。Reactの例と大きく異なる点として、レンダリング時に2つのコンポーネントから一方を選ぶために、JavaScriptの三項条件演算子を使っています。次のコマンドでアプリを起動できます。

npm run dev

アプリが起動すると、http://localhost:3000に次のような画面が表示されます。

localhost:3000のブラウザページに「You are logged out」と表示され、「Log In」ボタンがある

もう一度、Log Inボタンをクリックして状態を変更してみましょう。

「You are now logged in」と表示され、「Log Out」ボタンがあるブラウザページ

Next.jsで条件付きルーティングを実装する方法

Next.js 10以降では、条件付きルーティングも簡単に実装できます。まず、conditionals-next/pagesディレクトリに新しいprofile.jsファイルを作成し、次のコードスニペットを貼り付けます。

// This is the profile page
const Profile = () => {
  return <div>This is your profile</div>
}

// This function is called on the server side before the page is rendered
// Redirecting from this method means that the user will not see the page even for a flash of a second (which often happens in client-side redirection)
export async function getServerSideProps(context) {
    const allowAccess = false; // This is usually deduced from the authentication state of the user

    if (!allowAccess) {

        console.log("Redirecting to home...")

        return {
            redirect: {
                permanent: false,
                destination: "/"
            }
        }
    } else
        return { props: {} }
}

export default Profile

次に、アプリを再度起動してhttp://localhost:3000/profileにアクセスします。すぐにホームページ(つまり/ルート)にリダイレクトされます。ターミナルでリダイレクトのログを確認できます。

# ... other output here
wait  - compiling...
event - compiled client and server successfully in 109 ms (199 modules)
wait  - compiling /profile (client and server)...
event - compiled client and server successfully in 114 ms (202 modules)
Redirecting to home...

allowAccessの値をtrueに変更すると、プロフィールページを表示できます。

localhost:3000/profileのブラウザー画面。黒いページに「This is your profile」という見出しが表示されている

Remixで条件付きレンダリングと条件付きルーティングを実装する

Remixは、Web標準を重視し、優れたユーザーエクスペリエンスの構築を支援する、成長中のもう1つのReactベースWebアプリ構築フレームワークです。Remixは2021年にオープンソース化され、Next.jsの有力な競合となりました。

Remixは完全なサーバーサイドレンダリングに対応したフレームワークで、エラーバウンダリーやトランジション処理などの機能を備えています。

Remixで条件付きレンダリングを実装する方法

RemixはReactをベースとしているため、ReactやNext.jsと同様の方法で条件付きレンダリングを実装できます。

まず、次のコマンドを実行して新しいRemixプロジェクトを作成します。

npx create-remix@latest

画面の指示に従ってプロジェクトを作成します。TypeScriptではなくJavaScriptを選択してください。プロジェクトを作成したら、app/routes/index.jsxに次のコードスニペットを貼り付けます。

import { useState } from "react";

// Define two components
const LoginButton = (props) => {
  return <>
    <div>You are logged out</div>
    <div><button onClick={props.toggleLoggedIn}>Log In</button></div>
  </>
}

const LogoutButton = (props) => {
  return <>
    <div>You are now logged in</div>
    <div><button onClick={props.toggleLoggedIn}>Log Out</button></div>
  </>
}

export default function Index() {
  // Define a state that the condition will be based upon
  const [loggedIn, setLoggedIn] = useState(false);

  // Define a function that can change the condition value
  const toggleLoggedIn = () => {
    setLoggedIn((currState) => {
        return !currState;
    })
  }

  // Use a switch statement to render one of two components based on login state
  switch(loggedIn) {
    case true: return <LogoutButton toggleLoggedIn={toggleLoggedIn} />
    case false: return <LoginButton toggleLoggedIn={toggleLoggedIn} />
    default: return null
  }
}

このコードスニペットには、各ステップで何が行われるかを説明するインラインコメントが含まれています。この例ではif...else文や三項条件演算子の代わりに、switch文を使ってレンダリングするコンポーネントを決定しています。

これまでの例と同様に、ホームページはhttp://localhost:3000で次のように表示されます。

localhost:3000のブラウザページ。「ログアウトしています」と「ログイン」ボタンが表示されている

これまでと同様に、Log Inボタンをクリックして状態を変更してみましょう。

「ログインしました」と「ログアウト」ボタンを表示しているブラウザページ

Remixで条件付きルーティングを実装する方法

Next.jsでサーバーサイドの条件付きルーティングを実装したのと同じように、Remixでも実装できます。conditionals-remix/app/routesディレクトリにprofile.jsxファイルを新しく作成し、次のコードを貼り付けます。

import { redirect } from '@remix-run/node'

const Profile = () => {
    return <div>This is your profile</div>
}

export const loader = () => {
    const allowAccess = false; // This is usually deduced from authentication state of the user

    if (!allowAccess) {
        console.log("Redirecting to home...")

        return redirect('/')
    }

    return null
}

export default Profile

ここではgetServerSideProps関数の代わりにloader関数を使います。Remixは、サーバー上でコンポーネントをレンダリングする際のデータ読み込みにloaderをサポートしており、これはNext.jsのgetServerSidePropsと同様の機能です。また、allowAccessがfalseの場合にリダイレクトを開始するため、ユーティリティ関数redirect(Next.jsのredirect関数に相当)を返します。

http://localhost:3000/profileにアクセスすると、もう一度すぐにホームページ(つまり/ルート)にリダイレクトされます。ターミナルでリダイレクトのログを確認できます。

# ... other output here
💿 Rebuilding...
💿 Rebuilt in 100ms
GET / 200 - - 17.179 ms
Redirecting to home...        #=== here it is
GET /profile 302 - - 7.107 ms
GET / 200 - - 6.564 ms

allowAccessの値をtrueに変更すると、プロフィールページを表示できるようになります。

localhost:3000/profileに、「This is your profile」という見出しが表示されたプロフィールページのブラウザーウィンドウ。

条件付きレンダリングに関するその他の考慮事項

条件付きレンダリングを使う際、仮想DOMへの影響が気になるかもしれません。条件付きレンダリングのさまざまな方法でパフォーマンスに違いはあるのでしょうか?条件付きレンダリングの詳細を隠すために新しいコンポーネントや高階コンポーネント(HOC)を作ると、アプリのDOMやパフォーマンスに影響するのでしょうか?このセクションでは、こうした疑問への答えを解説します。

条件付きレンダリングとJSX

条件付きレンダリングの実装方法によって、アプリのDOMやパフォーマンスに影響が出ることはありません。次の例を見てみましょう。

if (loggedIn)
  return <button>Log Out</button>
else
  return <button>Log In</button>

三項条件演算子を使って、次のように書くこともできます。

return <button>{loggedIn ? "Log Out" : "Log In"}</button>

どちらの場合も、親コンポーネントが2つのコンポーネント(つまりLoginButtonとLogoutButton)のうち一方を返し、DOMにはまったく同じ変更が生じます。どちらの場合も異なるボタンタグのインスタンスが返されるのでは、と考えるかもしれませんが、それは誤りです。ボタンなどのJSX要素はインスタンスではなく、最終的なHTML構造を表す記述、つまり骨組みにすぎないからです。

同じ構造で同じ要素を返す限り、DOMへの影響は変わりません。つまり、JSXの構造が同じであれば、レンダリングを自由にコンポーネントの内部に抽象化できます。

これにより、構造がまったく同じ2つのコンポーネントが1つの状態を共有し、そのうち一方だけがDOMの同じ場所で条件付きレンダリングされる、といった興味深い状況も生まれます。詳しくはReactのWebサイトをご覧ください。JSXの単純な三項演算子がアプリの動作にどれほど大きな影響を与えるかが紹介されています。

ネストされたコンポーネントと条件付きレンダリング

コンポーネントの定義をコンポーネント内にネストするのは、アンチパターンと見なされています。主な理由の一つは、DOMツリー内の位置にかかわらず、親コンポーネントが再レンダリングされるたびに、ネストされたコンポーネントが再作成されることです。この動作は先ほど見たものとは異なります。ランタイムでは、親コンポーネントの再レンダリング時にネストされたコンポーネントの定義が再作成されるためです。これは、ネストされたコンポーネントの定義の新しいインスタンスが作成されるのに近く、それがこの問題を引き起こします。

まとめと参考リソース

条件付きレンダリングは一見すると(実際にも)とてもシンプルですが、Reactアプリのパフォーマンスとユーザー体験を向上させる強力な手法です。この記事では、そのメリットや条件付きルーティングとの違いに加え、React、Next.js、Remixでの実装方法を紹介しました。

条件付きレンダリングや、レンダリング間でReactが状態をどのように扱うかについて詳しく知りたい方は、ぜひ新しいReactドキュメントをご覧ください。

Reactアプリの開発では、ルーティングや状態管理などの機能を追加するために、npmパッケージを選んでインストールすることがよくあります。Snyk Advisorを活用すれば、アプリの高いセキュリティ基準を維持しながら、プロジェクトに最適なnpmパッケージを見つけられます。また、Reactの開発で実践すべきセキュリティのベストプラクティス10選もご覧ください。

SnykはIDE拡張機能としても利用でき、Reactのコードベースに潜む安全でないコードを見つけたり、オープンソースの依存関係にあるセキュリティ上の脆弱性の修正に役立ちます。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。

続きを読む

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

AIコーディングエージェントが不適切なアクセス制御を繰り返し実装する理由

AIコーディングエージェントは、コンパイルが通りレビューも通過する一方で、あるテナントのデータを別のテナントに公開してしまう認可ロジックを生成することがあります。不適切なアクセス制御が検出しにくい理由と、その防止策をご紹介します。

Blog

Evo ADSのエージェント動作ガバナンスが一般提供開始:MCPの利用を管理

Evo ADSのエージェント動作ガバナンスが、MCPガバナンスから一般提供を開始しました。主要なAIコーディングエージェント全体で、MCPサーバーの利用を検出、承認、監視、記録、ブロックできます。