brand logo

ドキュメント

useState

useState コンポーザブルは、リアクティブで SSR フレンドリーな共有状態を作成します。

使用法

// リアクティブな状態を作成し、デフォルト値を設定
const count = useState('counter', () => Math.round(Math.random() * 100))
こちらも参照 getting-started > state-management

useState 内のデータは JSON にシリアライズされるため、クラス、関数、シンボルなど、シリアライズできないものを含まないことが重要です。

useState はコンパイラによって変換される予約関数名であるため、自分の関数に useState という名前を付けるべきではありません。

shallowRef の使用

状態を深くリアクティブにする必要がない場合は、useStateshallowRef と組み合わせることができます。これにより、状態に大きなオブジェクトや配列が含まれる場合のパフォーマンスが向上します。

const state = useState('my-shallow-state', () => shallowRef({ deep: 'not reactive' }))
// isShallow(state) === true

useState<T>(init?: () => T | Ref<T>): Ref<T>
useState<T>(key: string, init?: () => T | Ref<T>): Ref<T>
  • key: データフェッチがリクエスト間で適切に重複排除されることを保証する一意のキー。キーを指定しない場合、useState のインスタンスのファイルと行番号に固有のキーが生成されます。
  • init: 状態が初期化されていないときに初期値を提供する関数。この関数は Ref を返すこともできます。
  • T: (TypeScript のみ)状態の型を指定します。

tips

このセクションは公式ドキュメントの翻訳ではなく、本サイト独自の補足記事です。

NuxtのuseStateとは?〜SSR対応のリアクティブ状態管理の強み〜

NuxtのuseStateは、サーバーサイドレンダリング(SSR)環境でも安全に使えるリアクティブな状態管理の仕組みです。Vueのrefreactiveと似ていますが、Nuxt特有の仕組みで、状態をコンポーネント間やページ間で共有しつつ、SSR時のデータの整合性やクライアントへのシリアライズを自動的に処理してくれます。

これにより、従来のVueアプリケーションでありがちな「SSR時に状態が同期しない」「クライアントで状態が初期化されてしまう」といった問題を解決し、開発者はよりシンプルに状態管理を実装できます。

まず結論:useStateのポイント

  • SSRとCSRの両方で状態を共有可能
    サーバーで生成した状態をクライアントに安全に引き継げる。

  • リアクティブなRefを返す
    Vueのrefと同様にリアクティブなので、UIの自動更新が可能。

  • キーによる状態の一意管理
    状態はキーで管理され、同じキーを使うと同じ状態を参照できる。

  • 初期値は関数で遅延評価
    初期化関数を渡すことで、必要なタイミングで初期値を生成。

  • シリアライズ可能なデータのみ対応
    クラスや関数、シンボルなどは避ける必要がある。

いつ使うべきか?使わない方がよいケースは?

使うべきケース

  • ページ間やコンポーネント間で状態を共有したいとき
    例えば、ユーザーのログイン状態やテーマ設定など、アプリ全体で共通の状態を管理したい場合。

  • SSR対応が必要な状態管理
    サーバーで生成したデータをクライアントに引き継ぎたい場合に最適。

  • 軽量な状態管理を求めるとき
    VuexやPiniaのような大規模な状態管理ライブラリを使わずに済ませたい場合。

使わない方がよいケース

  • 複雑な状態管理や多機能なストアが必要な場合
    大規模なアプリで複雑なロジックやミドルウェアが必要なら、PiniaやVuexのほうが適している。

  • シリアライズできないデータを扱う場合
    クラスインスタンスや関数、シンボルなどはuseStateでは管理できない。

  • 状態の永続化や外部ストレージ連携が必要な場合
    useStateはあくまでメモリ上の状態管理なので、永続化は別途実装が必要。

実務でよくあるユースケースとサンプルコード

1. カウンターの状態共有

複数のコンポーネントでカウンターの値を共有し、どこからでも増減できる例です。

// composables/useCounter.ts
export const useCounter = () => {
  const count = useState<number>('counter', () => 0)
  const increment = () => {
    count.value++
  }
  return { count, increment }
}
<template>
  <button @click="increment">Count: {{ count }}</button>
</template>

<script setup lang="ts">
const { count, increment } = useCounter()
</script>

2. ユーザーのログイン状態管理

ログイン情報をSSRで取得し、クライアントに引き継ぐ例です。

// composables/useUser.ts
export const useUser = () => {
  const user = useState<User | null>('user', () => null)
  const login = (userInfo: User) => {
    user.value = userInfo
  }
  const logout = () => {
    user.value = null
  }
  return { user, login, logout }
}

サーバーサイドで認証情報を取得し、初期化することも可能です。

3. 大きなオブジェクトを浅くリアクティブに管理

パフォーマンス向上のためにshallowRefと組み合わせる例。

const state = useState('my-shallow-state', () => shallowRef({ deep: 'not reactive' }))

この方法は、深いリアクティブ追跡が不要な大きなデータ構造に有効です。

よくある落とし穴・注意点

1. シリアライズできないデータの扱いに注意

useStateの状態はJSONにシリアライズされてクライアントに送られるため、関数やクラスインスタンス、シンボルなどは含められません。これらを含むとエラーや予期せぬ動作の原因になります。

2. キーの一意性を保つこと

状態のキーはユニークである必要があります。同じキーを意図せず複数箇所で使うと状態が衝突し、バグの原因になります。特に動的に生成する場合は注意しましょう。

3. SSRとCSRの状態の差異に注意

サーバーで生成した状態とクライアントでの初期状態が異なるとHydrationエラーが発生します。初期値はサーバー・クライアントで同じになるように設計してください。

4. パフォーマンスへの影響

大量の状態を深くリアクティブにするとパフォーマンスが低下することがあります。必要に応じてshallowRefを使うなど工夫しましょう。

まとめ

NuxtのuseStateは、SSR対応のリアクティブな状態管理を簡単に実現できる強力なツールです。ページ間やコンポーネント間で状態を共有しつつ、サーバーとクライアント間のデータ整合性を保てるため、Nuxtアプリケーションの開発効率を大きく向上させます。

ただし、シリアライズ可能なデータに限定されることやキーの管理、Hydrationの注意点などを理解し、適切に使うことが重要です。実務では軽量な状態管理やSSR対応が必要なケースで特に有効なので、ぜひ活用してみてください。

useStateはVueのrefと似ていますが、NuxtのSSR環境に最適化されている点が最大の特徴です。VueのリアクティブAPIと組み合わせて使うことで、より柔軟な状態管理が可能になります。