6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue.js Composableの本質は「再利用性」ではなく「責任分離」にある

Posted at

今回のテーマ

Vue.jsでコンポーネントのロジックを抽出する際、Composableは非常に強力なパターンです。
しかし、「このロジックは1箇所でしか使わないから、Composableにする必要はない」と判断していませんか?
実は、この考え方こそが、Composableの本質を見誤る典型的な罠なのです。
本記事では、Composableの定義から紐解き、なぜ「責任分離」こそが重要なのかを解説します。

Composableの定義とよくある誤解

Vue.js公式ドキュメントでは、Composableを次のように定義しています。

状態を持つロジックをカプセル化して再利用するための関数

この定義において、「再利用」という言葉が強調されるため、多くの開発者が「複数箇所で使われるかどうか」を判断基準にしてしまいます。

よくある思考パターン

「このロジックは今のところこのコンポーネントでしか使わないから、Composableにしなくていいか」

一見合理的に思えるこの判断ですが、実は大きな落とし穴があります。

「再利用しない」の裏にある本当の問題

「再利用しないだろう」と考えてしまう場合、その多くは責任分離が不十分である可能性が高いです。

責任過多が引き起こす問題

責任分離が不十分なロジックは、以下のような特徴を持ちます:

  1. 特定の場面に最適化されすぎている: 「このコンポーネントの、この状況で使う」ことを前提に設計されている
  2. 複数の関心事が混在している: データ取得、状態管理、UI制御などが1つのロジックに詰め込まれている
  3. 暗黙の依存関係がある: コンポーネント固有の状態や外部要因に依存している

結果として、そのロジックは特定のコンテキストでしか機能せず、汎用性が失われます。
そして「やっぱり再利用できなかった」という結論に至るのです。

責任分離がもたらす自然な再利用性

適切に責任分離されたロジックは、自然と再利用可能な形になります。
これは「再利用を目的として設計する」のではなく、「単一責任の原則に従った結果、再利用可能になる」という順序が重要です。

責任分離の実践例

以下は、責任が混在したComposableの例です:

// 悪い例: 責任が混在している
export function useUserProfile() {
  const user = ref(null)
  const isLoading = ref(false)
  const errorMessage = ref('')
  const showNotification = ref(false)

  async function fetchUser(userId) {
    isLoading.value = true
    errorMessage.value = ''
    try {
      const response = await fetch(`/api/users/${userId}`)
      const data = await response.json()
      user.value = {
        ...data,
        displayName: `${data.firstName} ${data.lastName}`, // UI表示用の加工
        avatarUrl: data.avatar || '/default-avatar.png' // デフォルト値の設定
      }
      showNotification.value = true // 通知の表示制御
    } catch (error) {
      errorMessage.value = 'ユーザー情報の取得に失敗しました'
    } finally {
      isLoading.value = false
    }
  }

  return { user, isLoading, errorMessage, showNotification, fetchUser }
}

このComposableは、データ取得、データ加工、エラーハンドリング、UI状態管理という複数の責任を持っています。

責任分離後のコード

これを適切に分離すると、以下のようになります:

// 良い例1: データ取得に特化
export function useFetch(url) {
  const data = ref(null)
  const isLoading = ref(false)
  const error = ref(null)

  async function execute() {
    isLoading.value = true
    error.value = null
    try {
      const response = await fetch(url)
      data.value = await response.json()
    } catch (e) {
      error.value = e
    } finally {
      isLoading.value = false
    }
  }

  return { data, isLoading, error, execute }
}

// 良い例2: ユーザーデータの加工に特化
export function useUserFormatter(user) {
  const displayName = computed(() => 
    user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
  )
  
  const avatarUrl = computed(() => 
    user.value?.avatar || '/default-avatar.png'
  )

  return { displayName, avatarUrl }
}

// 良い例3: 通知状態の管理に特化
export function useNotification() {
  const isVisible = ref(false)

  function show() {
    isVisible.value = true
  }

  function hide() {
    isVisible.value = false
  }

  return { isVisible, show, hide }
}

分離のメリット

責任を分離することで、以下のような利点が得られます:

  • useFetch: API通信全般で再利用可能
  • useUserFormatter: ユーザー情報を表示する全ての場所で再利用可能
  • useNotification: あらゆる通知シーンで再利用可能

それぞれが単一の責任を持つため、予測可能で、テストしやすく、そして自然と再利用可能になっています。

再利用性の本当の意味

ここで重要なのは、「再利用性」を狭く捉えすぎないことです。

再利用性が持つ複数の側面

  1. コード的な再利用: 複数のコンポーネントで同じComposableを使用する
  2. テスト容易性: ロジックを独立してテストできる
  3. 保守性: 変更の影響範囲が明確で、修正しやすい
  4. 理解しやすさ: 単一責任なので、コードを読む際の認知負荷が低い

たとえ現時点で1箇所でしか使われていないComposableでも、適切に責任分離されていれば、上記のメリットは全て享受できます。

まとめ

Composableを作成する際の判断基準は、「複数箇所で使うかどうか」ではなく、「適切に責任分離されているか」です。
以下は、本記事のポイントです。

  • Composableの定義: 「状態を持つロジックをカプセル化して再利用するための関数」
  • よくある誤解: 「再利用しないからComposableにしない」という判断は、責任分離の不足を示唆している
  • 正しいアプローチ: 単一責任の原則に従ってロジックを分離すると、自然と再利用可能な形になる
  • 再利用性の本質: コードの再利用だけでなく、テスト容易性、保守性、理解しやすさも含まれる

「再利用されるかどうか」を気にするのではなく、「このロジックは単一の責任を持っているか」を問いかけることで、結果的にComposableの定義を満たす、質の高いコードが生まれます。

6
1
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
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?