Open1

フロントで使える軽量なLoadable型

Okoha YugureOkoha Yugure

コンポーネントレベルでCSRしたいって時に、

interface LoadableInterface {
  isUnloaded: boolean
  isLoaded: boolean
  isFailed: boolean
}

interface Unloaded extends LoadableInterface {
  isUnloaded: true
  isLoaded: false
  isFailed: false
}

interface Loaded<T> extends LoadableInterface {
  isUnloaded: false
  isLoaded: true
  isFailed: false
  data: T
}

interface Fail<U> extends LoadableInterface {
  isUnloaded: false
  isLoaded: false
  isFailed: true
  error: U
}

export const newUnloaded = (): Unloaded => {
  return {
    isUnloaded: true,
    isLoaded: false,
    isFailed: false,
  }
}

export const newLoaded = <T>(data: T): Loaded<T> => {
  return {
    isUnloaded: false,
    isLoaded: true,
    isFailed: false,
    data,
  }
}

export const newFail = <U>(error: U): Fail<U> => {
  return {
    isUnloaded: false,
    isLoaded: false,
    isFailed: true,
    error,
  }
}

export type Loadable<T, U = unknown> = Unloaded | Loaded<T> | Fail<U>

こんな感じでLoadableを定義して、例えばvueなら

<script lang="ts" setup>
    const loadable = ref<Loadable<SomeModel>>(
      newUnloaded()
    )

    someClient
      .someRequest()
      .then((res) => {
        loadable.value = newLoaded(res.data)
      })
      .catch((err) => {
        loadable.value = newFail(err)
      })
</script>
<template>
    <loading v-if="loadable.isUnloaded" />
    <div v-if="loadable.isFailed"> データの取得に失敗しました </div>
    <div v-if="loadable.isLoaded">
        {{loadable.data}}
    </div>
</template>

とやると綺麗にローディングとエラーがハンドリングできるな、とか思いついたので試してるけど今のところいい感じ