see also: Responding to Events – React
ネストされたコンポーネントの親に設定されているイベントハンドラは、子コンポーネントに対するイベントも拾う動作となります。これを Event Propagation と呼びます。子コンポーネントのイベントハンドラで e.stopPropagation()
を呼ぶと親コンポーネントへのイベントの伝播を止めることができます。
いくつかのコンポーネントにはブラウザデフォルトの振る舞いが設定されているものがあります。例えば form
タグについては子コンポーネントで submit イベントが発生するとページのリロードが発生してしまいます。これを防ぐには submit イベントが発生するコンポーネントのイベントハンドラで e.preventDeafult()
を呼び出す必要があります。
see also: Render and Commit – React
React におけるページ表示内容の更新は次のような流れで行われます。
単純に初期画面表示以降のみに絞ると React のレンダリングは、コンポーネントのステートが更新されたことをきっかけとして、該当関数コンポーネント自体が再実行され、その計算結果が現在の DOM のコンポーネントと違う場合のみ変更を適用するという動作となります。
'use client'
は、どのコードがクライアントで実行されるかをマークするディレクティブです。'use server'
は、クライアントサイドのコードから呼び出せる、サーバサイドの関数をマークするディレクティブです。see also: 'use client' ディレクティブ – React
see also: 'use server' ディレクティブ – React.
see also: React Labs: 私達のこれまでの取り組み - 2023年3月版 – React
React では基本的にクライアントサイドでレンダリングされますが、React チームにより設計された新しいアプリケーションアーキテクチャである React Server Components を使用するアプリではサーバーサイドでレンダリングされます。Next.js の App Router では React Server Components をプリミティブとして採用しており、従来の Pages Router を使ったアプリケーションの構成とはやや考え方が異なります。
Pages Router では基本的にページを単位として SSG, SSR, ISR というレンダリング方法がありましたが、App Router ではコンポーネント単位でサーバーサイドでレンダリングするか、クライアントサイドでレンダリングするかを決定し実装していきます。
クライアントコンポーネントとサーバーコンポーネントの違いは単純にクライアントサイドとサーバーサイドのどちらでレンダリングされるかという点となります。
React Server Components をプリミティブとする App Router ではデフォルトはサーバーコンポーネントとなり、クライアントコンポーネントを作るためにはファイルの冒頭に 'use client'
ディレクティブを記述する必要があります。
実際に下記のように非常に単純なクライアントコンポーネントとサーバーコンポーネントを定義し、それらを組み込んだページを表示してみることにより、それぞれのコンポーネントの基本的な動作を確かめることができます。
// ClientComponent.tsx 'use client' import { Typography } from '@mui/material' import { useEffect, useState } from 'react' const ClientComponent = () => { const [time, setTime] = useState<string>() useEffect(() => { setTime(new Date().toLocaleString('ja-JP')) }, []) console.log('this is a client component') return <Typography variant='body1'>Client Component(Updated at {time})</Typography> } export default ClientComponent // ServerComponent.tsx import { Typography } from '@mui/material' const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const ServerComponent = async () => { await sleep(1500) console.log('this is a server component') const time = new Date().toLocaleString('ja-JP') return <Typography variant='body1'>Server Component(Updated at {time})</Typography> } export default ServerComponent // page.tsx import ClientComponent from './ClientComponent' import ServerComponent from './ServerComponent' const AppDir = async () => { return ( <> <h1>Hello</h1> <ServerComponent /> <ClientComponent /> </> ) } export default AppDir
上記を実行すると Next.js の実行ログに this is a server component
がログ出力され、ブラウザに this is a client component
がログ出力されます。
ページ、レイアウト、ルートハンドラなどは基本的にサーバー側でビルド時にレンダリングされ、デフォルトではキャッシュされますが、毎回サーバー側でレンダリングを発生させたり、ISR 的な動作で一定間隔おきに再レンダリングしたい場合は revalidate オプションを指定する形となります。
export const revalidate = 10
一般的にはサーバーコンポーネントがサーバー上でレンダリングしている間、UI にはなんらかのローディング表示が必要となることが多々あります。CSR ではそのようなときローディング状態管理を行いその状態に応じてスケルトンを表示するなどの対応が必要でした。
App Router のサーバーコンポーネントと Suspense
という機能を利用するとこのようなローディングの状態の処理が非常に簡単になります。例えば、下記のようにレンダリングに 3 秒かかるコンポーネントがあるとすれば、それを利用する側にて Suspense を用いると fallback 時のコンポーネントを簡単に指定できます。
// ServerComponent.tsx import { Typography } from '@mui/material' const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const ServerComponent = async () => { await sleep(1500) console.log('this is a server component') const time = new Date().toLocaleString('ja-JP') return <Typography variant='body1'>Server Component(Updated at {time})</Typography> } export default ServerComponent // page.tsx import { Suspense } from 'react' import ClientComponent from './ClientComponent' import ServerComponent from './ServerComponent' const AppDir = async () => { return ( <> <h1>Hello</h1> <Suspense fallback={<p>Loading...</p>}> <ServerComponent /> </Suspense> <ClientComponent /> </> ) } export default AppDir
see also: File Conventions: Route Segment Config | Next.js
Page, Layout, Router Handler の振る舞いを左右するオプション
authorization ヘッダーを利用して API キーベースの認証を導入する方法がある
Authorization flow To securely trigger API routes and serverless functions with Github Actions, you need to provide an authorization key in the header of your API call, which, when executed, gets compared to a corresponding key in your Next.js application. You can achieve this by adding Encrypted Secrets to your Github repository and passing them with the header of your HTTP request, like shown in the previous code snippet. Along with adding the key to your Github repository, you also need to access it within your Next.js application, preferably through Environment Variables. How to secure serverless functions?? · vercel · Discussion #1833
Vercel Cron Jobs と組み合わせるときには CRON_SECRET という環境変数を利用するスペシャルな手法 が取れるが、Hobby プランは 1 invocation/day という厳しい制約があり、まあ API Key + EventBridge 使うことになるかなと思う
ウェブ界隈でエンジニアとして労働活動に励んでいる @gomi_ningen 個人のブログです