Composition APIのSSRモードでのuseFetchとストアゲッター越しのcomputedで警告が出ていたので解決した方法をメモ。

Option APIの時はcomputedでの参照に特に何も問題はなかったが、Composition APIで同じようなことをしようとすると

[Vue warn]: Write operation failed: computed value is readonly.

という警告文が出るようになる。

よく調べていくとこれはライブラリ側のエラーのようで長い間対応されていないみたい。(2021年9月現在)
細かい原因までは深く追求していないけど、見た感じ初期化のタイミングで内部的に変更不可な値を変更するような処理が走ってエラーが出てるっぽいので、computedにセッターを追加してv-modelのパターンに直してやったら解決した。

以下が抜粋。

store/item.ts

export const state = (): ItemState => ({
  items: [],
})

export const getters = {
  getItems(state) {
    return state.items
  },
}

export const mutations = {
  setItems(state, items) {
    state.items = items
  }
}

export const actions = {
  fetchItems({ commit }) {
    return axios.get('/items').then(response => commit('setItems', response.data.items))
  }
}

pages/items.vue

<template>
...
</template>

<script lang="ts">
import {
  defineComponent,
  SetupContext,
  WritableComputedRef,
  useFetch,
  reactive,
  computed,
  toRefs,
} from '@nuxtjs/composition-api'

type State = {
  items: WritableComputedRef<ItemState['items']>
}

export default defineComponent({
  setup () {
    const { store } = useContext()

    useFetch(async () => {
      await store.dispatch('item/fetchItems')
    })

    const state = reactive<State>({
      items: computed({
        get: () => store.getters['item/getItems'],
        set (items) {
          store.commit('item/setItems', items)
        }
      })
    })

    return {
      ...toRefs(state),
    }
  }
})
</script>

別の解決策としてmiddlewareの活用も見当たったが、ライフサイクル周りの変更は処理フローに影響を与えてしまう可能性があるのに比べて、こっちの方法だとコード量は多くなるものの、形を大きく崩さずに済む分まだ使えるのではと思う。

もう少し工夫すればもっと簡略化できるはずなので他によいやり方見つかったら教えてください。

というよりこればっかりは移行時のクリティカルな問題となりそうなのでライブラリさんできるだけ早く対応しろください。

プロフィール画像

ふじわら

よくわからないもので戯れてたら自分のことすらよくわからない人間になってしまいました。

ひっそりYouTubeしてます。