くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Nuxt3のComposablesとuseStateと状態管理

Nuxt3をいろいろ試していたときの備忘録。
Vuexが含まれなくなり、ComposablesやuseStateの登場で、
大きく変わっている印象。

composablesディレクトリとは?

Composablesとは?

Vue3のドキュメントだとこのあたり。
Composables | Vue.js

In the context of Vue applications, a "composable" is a function that leverages Vue Composition API to encapsulate and reuse stateful logic.

"composable"は、VueのComposition APIを活用したstatefulな関数

ぱっと分かりづらいけど、Reusability(再利用性)のカテゴリに入っていて、
状態をもつ関数が"composable"という感じ。

例では、マウスやlocalなどが紹介されている。
Nuxt 3 - State Management

useStateとは?

  • SSR-friendlyなコンポーネント間で共有できるステートのcomposable
  • SSR-friendlyなref()の代替え
  • その値は、サーバー側のレンダリング後(クライアント側のハイドレーション中)に保持され、
    一意のキーを使用してすべてのコンポーネント間で共有される
  • useStateは、setup()内か、Lifecycle Hooksでしか利用できない

Nuxt3のドキュメントだとこのあたり。
Nuxt 3 - State Management
Nuxt 3 - useState

useStateを使った状態管理

vuexのようなStoreのように、保持する状態(state)と
状態を操作するActionみたいな感じにしている。

// ~/composables/counterStore.ts
import { Ref } from "nuxt/dist/app/compat/capi";

type CounterState = {
  count: number;
};

export const useCounterStore = () => {
  const state = useState<CounterState>("counter_state", () => ({
    count: 0,
  }));
  return {
    state: readonly(state),
    countUp: countUp(state),
    setCount: setCount(state),
  };
};

const countUp = (state: Ref<CounterState>) => {
  return () => state.value.count++;
};

const setCount = (state: Ref<CounterState>) => {
  return (count: number) => (state.value.count = count);
};

auto-importしてくれるので、使うときはこんな感じ。

<script lang="ts" setup>
const counterStore = useCounterStore();
const { state } = counterStore;
</script>

<template>
  <div>
    <div>{{ state.count }}</div>
    <button @click="counterStore.countUp">countUp</button>

    <button @click="counterStore.setCount(0)">reset</button>
  </div>
</template>

TypeScriptのクラスで書いてもいけそうだった

import { Ref } from "nuxt/dist/app/compat/capi";

type CounterState = {
  count: number;
};

export const useCounterStore = () => {
  return new CounterStore();
};

class CounterStore {
  _state: Ref<CounterState>;
  constructor() {
    this._state = useState<CounterState>("counter_state", () => ({
      count: 0,
    }));
  }

  get state() {
    return readonly(this._state);
  }

  countUp() {
    this.setCount(this._state.value.count + 1);
  }

  setCount(count: number) {
    this._state.value.count = count;
  }
}

"counter_state"のように保持する状態のkey/idを指定するので、
全体で重複がないように気をつけないといけない。


以上!! 状態管理周りも理解できてきた気がする(*´ω`*)

参考にしたサイトさま