0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsのチュートリアル感想戦(自分用学びメモ)

Posted at

はじめに

自分用の、Learn Next.jsをやってみてのメモです(GitHubのページ)
なるほどな~って思った事を書いてます

後半にすすむにつれ、意味が分からなくなり、雑になっていき、特にCh13以降(エラー処理・フォームバリデーション・認証と認可)は意味不明だったので、おいおい出来たら良いな~という気持ちです

(一応vercelにも上がってますが、nextauthの認証がタイムアウトするため動かないです...😢)

Ch3: Optimizing Fonts and Images

Image

Ch4: Layouts and Pages

Nested Routing

  • フォルダ配下の page.tsx は特別で、例えば app/page.tsx/, app/oyo/page.tsx/oyo にアクセスすると表示される
    image.png

layout

  • layout.tsx は他のページと共有するUIを作れる
    image.png
  • page コンポーネントは再レンダリングされるけど、レイアウトはされないので、(たぶん効率が良い)

Ch5: Navigating Between Pages

Link

usePathname()

clsx

className={clsx(
    'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
    {
    'bg-sky-100 text-blue-600': pathname === link.href,
    },
)}
  • pathname === link.hrefの時に、追加でbg-sky-100 text-blue-600が追加される

Ch.6: Setting Up Your Database

  • データを DB に登録することをseedingと呼ぶ

Ch.7: Fetching Data

Choosing how to fetch data

API

データベースクエリ

React Server Components

どうやら新しい機能らしい。デフォで使われてる

メリット

  • 非同期処理が楽になる?useEffect/useStateを使わなくても、async/awaitが使える?
  • サーバーで実行されるから
    • 結果のみクライアントに送られる(データ・ロジックはサーバーのみが保持できる)
    • 直接 DB をクエリ操作できる

問題点

  • request waterfallが発生する

    • 前のリクエストが終了したら、次のが実行される。並列処理されてない

      const revenue = await fetchRevenue();
      const latestInvoices = await fetchLatestInvoices(); // fetchRevenue()が終わるまで待つ
      const {
      numberOfInvoices,
      numberOfCustomers,
      totalPaidInvoices,
      totalPendingInvoices,
      } = await fetchCardData(); // fetchLatestInvoices()が終わるまで待つ
      
      • 対処法として、Promise.all()で並列に処理
        • どれかのリクエストがめっちゃ遅かったらどうなる...?
          • データを動的に読み込むのであれば、遅い処理があればそりゃページ読み込む速度遅くなるよね
      export async function fetchCardData() {
      try {
      const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
      const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
      const invoiceStatusPromise = sql`SELECT
              SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
              SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
              FROM invoices`;
      // Promise.all()で並列に処理
      + const data = await Promise.all([
          invoiceCountPromise,
          customerCountPromise,
          invoiceStatusPromise,
      ]);
      // ...
      }
      }
      

    image.png

  • static rendering(静的レンダリング)なので、データが変化しても表示は変化しない

    • SQL が問い合わせてる先のデータが削除・追加されても、そのつど SQL が実行されるわけじゃない

fetching data for the dashboard overview page

  • export default async function Page()asyncawaitを使えるようにする
  • const data = await sql<Revenue>`SELECT * FROM revenue`;で SQL が実行できそう

Ch.8: Static and Dynamic Rendering

  • 静的レンダリング:ユーザーが訪れるたびに、キャッシュされたのが提供される
  • メリット
    • 早い
    • サーバーの負担が少ない
    • SEO対策になる。クローラーが喜ぶ
  • 向いてる場所
    • データを使わないサイト
    • みんな共通のデータ(不変)を使う
  • 動的レンダリング
    • クッキーとか、URL のパラメータとかの、リクエスト時にのみ得られる情報にアクセスできる
    • ただ、データのフェッチに時間がかかると、ページ読み込みもその分遅くなる

Ch.9: Streaming

Stream について学ぼう

  • データを小さいまとまり(チャンク)にして、送れるぜ!になったら各々送る

    • 早くなるよ
      image.png
      image.png
  • 方法

  1. loading.tsx ファイル
  • loading.tsxは next.js のSuspenseの特別なファイルらしい
    • page.tsxみたいな、特殊な奴
  1. <Suspense>コンポーネント
  • 何らかの条件が満たされるまで(例:データが読み込まれるまで)一部のレンダリングを延期できる
  • 動的コンポーネントを Susppense でラップ、動的コンポーネントのロード中に表示するフォールバックコンポーネントを渡す

Ch.10: Partial Prerendering

  • 元々、ルート(ページ)全体を静的・動的レンダリングのどちらかに統一する必要があった

    • if you call a dynamic function in a route (like querying your database), the entire route becomes dynamic.
    • でも、動的・静的どっちも使いたいね
      • 大部分は静的に生成、一部は動的にデータ取得とか!
        image.png
  • Partial Prerendering(PPR): Next.js 14 で登場

    • Suspenseを使って、動的に読み込む場所のスペースを残して静的レンダリング->動的コンテンツを非同期に読み込みレンダリング
  • 使い方

    • Suspenseで動的なのをラップしてれば、基本的にコードを変えなくてもよい
// next.config.mjs
// 追記
const nextConfig = {
  experimental: {
    ppr: "incremental",
  }
};
// layout.tsx
export const experimental_ppr = true; // 追記
  • データフェッチを最適化しよう
    1. データベースのリージョンは近所に
    2. React Server Componentsを使うと、ロジック・データがサーバー内のみに存在する
      • クライアントの JS が頑張らなくていい
      • データがクライアントに漏れる心配ない
    3. 必要な分のデータは SQL で取得しよう
      • 毎回の通信するデータが減る
      • in-memory に変換するようの JS が減る
    4. データは並列に取得しよう
    5. Streaming で、重いデータが足を引っ張ってページがいつまでも表示されない...をなくそう
      • すべてロードされなくても、ユーザーが画面操作できるようにしたいね
    6. データフェッチを必要なコンポーネントに移すことで、どの部分をダイナミックにするかを分離できる

Ch.11: Adding Search and Pagination

  • Pagination?: 検索件数がたくさんあるときに、下に出てくるあれ
    image.png

なんでURL search paramを使う?

  • 他の方法として、クライアントの方でstate管理もできる
  • メリット
    1. ブックマークや、他の人にURLでシェアできる
    2. サーバーサイドレンダリングと初期読み込み:URLパラメータはサーバー上で直接処理できるから、初期の状態をレンダリングするとき便利
    3. 分析・トラッキング:クライアントでごちゃごちゃしなくても、クエリ・フィルターがURLに直接埋め込まれてるので、容易にユーザーの挙動を追跡できる

検索機能で使う機能

  • useSearchParams: URLのパラメータを取得可能
    • /dashboard/invoices?page=1&query=pending -> {page: '1', query: 'pending'}.
  • usePathName: 現在のURLを取得可能
  • useRouter: ページの遷移が出来る(?)

debouncing(ディバウンシング)

  • 関数の発火を制限する
    • 現状:ユーザーが一文字入力するたびに、検索している->サーバーが大変
    • 理想:ユーザーが入力を止めた時に、発火させたい
  • 原理
    1. 発火:発火したら、タイマーがスタート
    2. 待機:もし再度発火 & タイマーが時間切れじゃない -> タイマーリセット
    3. 実行:もしタイマーが切れたら、関数実行
      pnpm i use-debounce

Tip: defaultValue vs value

  • value: stateで入力値を管理してる
    • controlled components(?)にするため
    • reactがinputの状態を管理できる
  • defaultValue: stateで管理してない
    • inputのみが情報を管理・保持してる
<input
  className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
  placeholder={placeholder}
  onChange={(e) => {
    handleSearch(e.target.value);
  }}
  defaultValue={searchParams.get('query')?.toString()}
/>

useSearchParams hook vs searchParams props

  • useSearchParams: クライアントコンポーネント
  • searchParams: サーバーコンポーネント

Ch.12: Mutating Data (mutate: 変化する)

Server Actionとは

  • サーバー上でコードを非同期で実行できる
    • APIを作らなくてもよい!
    • クライアント/サーバーコンポーネントから実行可能
  • どうやら、セキュリティ面もいい感じらしい
    • encrypted closures, strict input checks, error message hashing, and host restrictions...

invoiceを作ろう

  • use server;: エクスポートされたのが、server actionになる。
    • クライアント・サーバーコンポーネントにて使用可能
  • formDataの情報を取得するためには、.get(name)を使う

    'use server';
    
    export async function createInvoice(formData: FormData) {
      const rawFormData = {
        customerId: formData.get('customerId'),
        amount: formData.get('amount'),
        status: formData.get('status'),
      };
    }
    
  • バリデーションしたい
    • typeof rawFormData.amount: 型が分かる
    • Zod: バリデーションライブラリ
    import { z } from 'zod';
    const rawFormData = {
      customerId: formData.get('customerId'),
      amount: formData.get('amount'),
      status: formData.get('status'),
    };
    
    • floatをなるべく扱わないようにしよう(intで管理できるのであれば、そうする)
  • client-side router cashe
    • これ、良く分からない

invoiceをアップデートしよう

  • Dynamic Route Seements

    • 正確な「segment name」を知らない & ルートをデータを基に作りたい時に良い (ブログタイトル、製品ページ...)
      image.png
  • server actionにはpropsを渡せないから、bindを使う必要がある

    • クライアントコンポーネントから実行されなくて、サーバーで実行されるため

Tip: UUID vs auto-incrementing key

  • auto-incrementing key: 短い
  • UUID: 長いけどたくさんのメリット
    • IDが衝突しない・ユニーク・列挙型攻撃に強い -> 大きいDBに向いてる

Ch.13: Handling Errors

error.js
notFound
not-found.js

error.tsx

  • エラーが発生したら表示するページ?
    • どんなエラーでも!
  • fallback(前の画面に戻る?)UIを表示する
  • クライアントコンポーネント(use client
  • 2つのpropsを受け取る
    • error: JSのError オブジェクト
    • reset:route segment(前の画面?)を再レンダリングする
  • ファイルを作って、中身を書く

notFound関数

  • リソースが存在しない時のみ
  • 下のように、エラーが出そうなところでハンドリング、同じ階層にnot-found.tsxを作成
    import { notFound } from 'next/navigation';
    if (!invoice) {
          notFound();
    }
    

Ch.14: Improving Accessibility

Form Validation

  • useActionState(クライアントコンポーネントで使う)
const FormSchema = z.object({
  id: z.string(),
  customerId: z.string({
    invalid_type_error: 'Please select a customer.',
  }),
  amount: z.coerce
    .number()
    .gt(0, { message: 'Please enter an amount greater than $0.' }),
  status: z.enum(['pending', 'paid'], {
    invalid_type_error: 'Please select an invoice status.',
  }),
  date: z.string(),
});
  • 色々な機能が、色々なファイルに散らばってて、何が何だか

Ch.15: Adding Authentication

Authetication vs Authorization

  • Authentication(認証):身分を証明する行為(ユーザー名とパスワード的な)
  • Authorization(認可): 認証された時、アプリをどこまで使っていいか決める(?)

NextAuth.js

  • 認証の機能を使える
    • セッションの管理
    • サインイン・サインアウト...
pnpm i next-auth@beta

下の値を.envのAUTH_SECRETに記入する

openssl rand -base64 32
  • やることがたくさんあって、何が何だか

memo

よくわからんエラー

Next.js (15.0.0-canary.56) is outdated (learn more)

Unhandled Runtime Error
Error: Unsupported Server Component type: undefined
  • これはpnpm add next@canaryしたら直った。
  • npm i next@latest, npm i next@canaryは無力だった

import の謎

import { lusitana } from '@/app/ui/fonts';import CardWrapper from '@/app/ui/dashboard/cards';は何が違う?
なんで二通りの書き方が存在する?

  • 前者:名前付きインポート
    • 下みたいに、モジュールが複数のエクスポート持つときに使う
    export const lusitana = 'some-value';
    export const anotherFont = 'another-value';
    
  • 後者:デフォルトインポート
    • モジュールが一つしかエクスポートしない時に使う
    const CardWrapper = () => { /* ... */ };
    export default CardWrapper;
    

use client?

  • イベントリスナーとフックを使えるらしい
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?