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

VueのProviderパターンを理解するためのサンプルコード

Posted at

はじめに

先日大変わかりやすいこちらの記事を拝見しました。

こちらの記事ではカウンターアプリをさまざまなパターンで実装しています。

  1. view, 状態, ロジックが混在している
  2. 状態とロジックを切り出す
  3. Providerパターンを使って状態を共有する

自分でもこの3パターンを書いて理解を深めようと思います。

環境

nuxt 3.9.3
vue 3.4.14
unocss 0.58.3

作るもの

シンプルなカウンターアプリを作成します。
初期値は0でプラスボタンをクリックすると1増えて、マイナスボタンをクリックすると1減ります。

view, 状態, ロジックが混在しているパターン

一つのファイルにview (<span>{{ count }}</span>), 状態 (const count = ref<number>(0)), ロジック (const increment = () => count.value++)が混在していますね。

pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <span>{{ count }}</span>
      <div flex="~ row" items="center" gap="8px">
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const count = ref<number>(0)

const increment = () => count.value++
const decrement = () => count.value--
</script>

状態とロジックを切り出すパターン

状態とロジックはuseCounterへ切り出しています。

composables/useCounter.ts
export function useCounter() {
  const count = ref<number>(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count,
    increment,
    decrement
  }
}
pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <span>{{ count }}</span>
      <div flex="~ row" items="center" gap="8px">
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </div>
  </div>
</template>

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

Providerパターン

Providerパターンを使って表示用のコンポーネント, インクリメントボタン, デクリメントボタン, ロジック (useCounter) に分けます。

composables/useCounter.ts
import type { InjectionKey } from "vue"

export const counterKey: InjectionKey<Counter> = Symbol('counter')

export function useCounter() {
  const count = ref<number>(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count,
    increment,
    decrement
  }
}

export type Counter = ReturnType<typeof useCounter>
components/counterDisplay.vue
<template>
  <div>
    <span>{{ counter?.count }}</span>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
components/counterIncrement.vue
<template>
  <div>
    <button @click="counter?.increment">+</button>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
components/counterDecrement.vue
<template>
  <div>
    <button @click="counter?.decrement">-</button>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <CounterDisplay />
      <div flex="~ row" items="center" gap="8px">
        <CounterIncrement />
        <CounterDecrement />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
provide(counterKey, useCounter())
</script>
1
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
1
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?