Closed13

React/Next.jsに入門する。

ymgnymgn

Vercel公式が良質なチュートリアルを用意してくれているので、こちらを先にやっても良さそう。
このチュートリアルを先にやるにしても、本を先に読むにしても、ゆくゆくは公式ドキュメントをじっくり読むようにすると良い(当たり前)。

公式チュートリアル

https://nextjs.org/learn/dashboard-app
https://nextjs.org/learn/react-foundations

公式ドキュメント

https://nextjs.org/docs

https://ja.react.dev/learn
https://ja.react.dev/reference/react

ymgnymgn

本の1章だけざっと読んだ(知ってることが大半なので流し読み)。

2023/11/11追記:
5章まで読んだ。

2023/11/12追記:
7章まで読んだ。

ymgnymgn

メモ:

  • Suspenseコンポーネント
    • 非同期を扱う
  • Profilerコンポーネント
    • 負荷計測
  • ポータル(ダイアログ表示などで使う)
  • react-error-boundary
    • Reactで提供されていないErrorBoundary(自前で"クラスコンポーネント"で実装する必要がある)をパッケージとして提供してくれている

TanStack QueryとSuspense, ErrorBoundaryの組み合わせがかなりシンプルに記述できていいな。

ymgnymgn

スキップすることにした章

  • 6章
    • MUI使わない
    • Storybookは今後ちゃんと使いたいので後に回してじっくり読む
  • 7章(フックは全部読む)
    • Recoil使わない
  • 8章
    • React Router使わない
  • 9章
    • テストは今後ちゃんと使いたいので後に回してじっくり読む
ymgnymgn

7章のフックが種類多い件。

  • 状態
    • useState
    • useReducer
      • 初期化関数を用意する場合は、第二引数、第三引数を活用して無駄なオーバーヘッドをなくす
    • useContext
      • グローバルな状態管理が可能
      • ロケール、タイムゾーン、テーマなど、アプリ全体で利用するような値の管理に利用する
  • エフェクト
    • useEffect(ページ描画後に非同期に実行される)
    • useInsertionEffect(アプリ開発者はまず使わない)
    • useLayoutEffect(ページが描画される前に同期的に実行される)
      • Popoverの表示位置を計算するなど、UIが描画される前にやっておきたい処理を実行するのに向くが、エフェクトの使用は極力避けるべき
    • 参照
    • useRef(濫用すべきでない)
    • useImperativeHandle
      • 子から親へメソッドを公開する(forwardRefと併用することが前提)
      • useRefよりも限定されたメソッドを親に強制できる
  • メモ
    • useMemo
    • useCallback
  • 遅延
    • useTransition
      • 優先度の低い更新を後回しにする
    • useDeferredValue
      • 特定の変数の遅延バージョンを生成する
  • その他
    • useSyncExternalStore
      • ローカルストレージ、オンラインステータスなどブラウザ側の状態を取得したいとき
    • useDebugValue
      • カスタムフック内の値を監視し、React Developer Toolsに出力
    • useId
ymgnymgn

useEffectは次のような状況でのみに使用を限定するべき。

  • ブラウザーAPI、文書ツリーへのアクセスを伴う操作
  • ネットワークからのデータ取得
  • 非Reactアプリで管理された領域との同期

公式ドキュメントにuseEffectが必要ないかも知れない例が載っていて、かなりためになる内容だった。
https://react.dev/learn/you-might-not-need-an-effect

ymgnymgn

fowardRef関数を使うと、配下の要素にRefオブジェクトを引き渡すことができる。

forwardRefってNext.jsの機能かとずっと勘違いしてた...。(import見れば'react'の機能だと分かるね)

// parent.tsx
const Parent = () => {
  const ref = useRef(null);

  useEffect(() => {
    ref.current?.focus();
  }, []);

  return (
    <Child label="" ref={ref} />
  );
}

// child.tsx
import { forwardRef } from 'react';

const Child = forwardRef(({ label }, ref) => (
  <label>
    {label}:
    <input type="text" ref={ref} />
  </label>
));
ymgnymgn

テクニック

コールバックRef

メモ化は積極的に行うべきか?

メモ化はしなくてもよいならばすべきではない。
メモ化そのもののオーバーヘッドがあるため、必ずしもメモ化がパフォーマンスを改善してくれるわけではない。
また、メモ化によってコードが複雑化する。

memo

Propsが変化した時にのみ再描画されるコンポーネントを作成する。

import { memo } from 'react';

export const MyButton = memo(({ id, handleClick, children }) => { ... });

useTransition vs. useDeferredValue

useTransitionのほうがpending管理もしやすいため優先して使用する。
外部ライブラリとの連携の都合上、useTransitionが使いにくい場合はuseDeferredValueを使用する。

カスタムフック

const useCounter = (init, step) => {
  const [state, dispatch] = useReducer(
    (state, action) => {
      switch (action.type) {
        case 'update':
          return { count: state.count + action.step };
        case 'reset':
          return { count: action.init };
        default:
          return state;
      }
    },
    {
      count: init
    }
  );
};

const handleUp = () => dispatch({ type: 'update', step });
const thandleDown = () => dispatch({ type: 'update', step: -step });
const handleReset = () => dispatch({ type: 'reset', init });
return [state, handleUp, handleDown, handleReset];

FC型は利用すべきか?

p.565を意訳:
結論から言うと不要。

理由:

  • propTypesは↓のようにPropsのtypeを記述して代用可
  • contextTypes, defaultPropsはTypeScript標準の記述で代用可
  • displayNameはデバッグツールであれば標準で関数名が表示されるため指定しなくても判別できる

↓のように、引数に直接Propsを書くことができる。

type Props = {
  children: ReactNode;
  name: string;
};

const HogeComponent = ({ children, name }: Props) => {
  return (
    <>
      <label>{name}</label>
      {children}
    </>
  );
};

YYYY-MM-DD形式の日付を生成

// スウェーデンのロケールで、標準でYYYY-MM-DD形式を採用していることから利用できるテクニック
const dateString = (new Date()).toLocaleDateString('sv-SE');
// -> 2023-11-13

これを使わずに意味的な正しさを求めるなら

const dateString = (new Date())
  .toLocaleDateString(
    'ja-JP',
    { year: 'numeric', month: '2-digit', day: '2-digit' },
  )
  .replaceAll('/', '-');
ymgnymgn

ReactでのTypeScript型(大事なところだけ抜粋)

import { ReactNode } from 'react';

type Props = {
  children: ReactNode
};

export const Component: FC<Props> = () => {
  ...
}
const name = useRef<HTMLInputElement>(null);

React関係ないけど、

Partial<T>
Readonly<T>
Record<K, T>
Exclude<T, U>
Extract<T, U>
NonNullable<T>
Parameters<T>
ReturnType<T>
ymgnymgn

App Router勉強メモ

src/appの構成

  • layout.tsx
  • page.tsx
  • loading.tsx
  • error.tsx
  • not-fount.tsx

↑が組み立てられて↓になるイメージ(p.574より)。
分かりやすい。

<Layout>
  <ErrorBoundary fallback={<Error />}>
    <Suspense fallbase={<Loading />}>
      <ErrorBoundary fallback={<NotFound />}>
        <Page />
      </ErrorBoundary>
    </Suspense>
  </Error>
</Layout>

Next.js既定のコンポーネント

  • Link
  • Script
  • Image

動的なメタデータの生成

generateMetadata関数を宣言することで、動的な値を使ってメタデータを生成することができる。

export async function generateMetadata({ params, searchParams }) {
  const result = await api.books.fetch(...);
  return {
    title: result.title,
    keywords: [result.author, result.publisher],
  };
}

Route Segment Config

ページごとにキャッシュの有効化など挙動を調整できる。

https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config

サーバーコンポーネント vs クライアントコンポーネント

極力サーバーコンポーネントを使用するべき。

サーバーコンポーネントのメリット

  • サーバーでのリソースを活用しやすくなる
  • ダウンロードすべきコードサイズを最小化できる
  • 機密情報(アクセストークン、APIキー)を管理しやすい

クライアントコンポーネントをどう置くか

Next.jsでは極力コンポーネントを細かく分けていき、クライアントコンポーネントはコンポーネント階層の下層に追いやるのが良い。

Next.jsのfetchメソッドの魔法

p.612より

Next.jsではfetchメソッドは便利に拡張されている。

  • 関数コンポーネントを非同期(async)にすることで、コンポーネント配下でfetchメソッドを実行できる
  • 個々のfetch呼び出しに際して、キャッシュ/再取得ルールを指定できる
  • コンポーネント階層で、同一のデータを取得しなければならない場合、リクエストの重複を除去する

ルータをプログラム的に操作する

useRouter関数でプログラム的にルータを操作できる。

サーバーアクション

フォームからのアクション、イベント処理などをサーバー側に委ねる仕組み。

'use server';

export const addTodo = async (data) => { ... }
export const editTodo = async (data) => { ... }

サーバーアクションは'use server'ディレクティブをファイルの頭に書くことで定義できる。

また、サーバーアクションはサーバーコンポーネントだけでなくクライアントコンポーネントからも呼び出すことが可能。

呼び出し方は何種類かある。

// 1. formのactionにサーバーアクションを指定して呼び出し
<form action={addTodo}>
  ...
  <button type="submit">...</button>
</form>

// 2. button要素のformAction属性に指定して呼び出し
<form>
  <button type="submit" formAction={editTodo}>
    ...
  </button>
</form>

// 3. イベントハンドラー経由で呼び出し
import { useTransition } from 'react';

const [isPending, startTransition] = useTransition();
<form>
  <button
    type="button"
    onClick={() => {
      startTransition(() => removeTodo(id));
    }}
  >
  </button>
</form>
このスクラップは2023/11/22にクローズされました