6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

redux-api-structをnpmに公開しました

Last updated at Posted at 2019-02-01

redux-api-struct

ライブラリを公開したのでqiitaに残しておきます。

僕は普段Reactを業務で書く場合に、

  • typescript
  • styled-component
  • redux
  • react-router
  • axios

あたりを使って書くのですが、このAPIに対してstoreの値をどのように持っておくかを簡単に定義することができるものをOSSにしました。

公開したものは 俺の考えたRedux + Atomic DesignでTypeSafeにコンポーネントを扱う方法という記事にでてくるもの何ですが、普段からよく書くのでライブラリにしておくと楽なので公開したといった具合です。

使い方

使い方は README.mdにも書いていますが、ざっくり書きます。

例えば、ブログサービスで記事詳細画面を作る場合を想定します。

modelは

interface Article {
  title: string
  body: string
}

とします。フロントエンドでやる流れとしては

  1. 画面を描画する(ローディング画面)
  2. APIにアクセスする
  3. 200 or 4xxが帰ってくる
  4. その状況に応じてUIを適応する

とざっくり仮定します。

この詳細の管理を articleというstateで管理するとします。

interface InitialState {
 article: Article
}
const initialState: InitialState = {
  article: null
}

さてこれを表示する画面を作成します。

import * as React from "react"

interface Props {
  article: Article
}
export const Article: React.FunctionComponent = ({ article }) => {
  return (
    <>
      <h1>{article.title}</h1>
      <div>{article.body}</div>
    </>
  )
}

ここまで書いておけば

<Article article={store.sampleReducer.article} />

これで、記事を表示するコンポーネントを定義することができました!

しかし、これだとAPIをfetchしている間、真っ白な画面がでてしまいます。ローディング画面を追加してみましょう。

ローディング画面を追加する

import * as React from "react"

export const Loading: React.FunctionComponent = () => (
  <p>now loading...</p>
)

ローディングコンポーネントを定義できたので、これを使って画面をつくりたいですね!

と、なった時に今の状況ではapiのfetchを行っている時の状態をstateで管理していないのでローディング画面をだすことができませんね。stateで管理しましょう。

interface InitialState {
  isFetching: boolean
  article: Article
}

これで、

import * as React from "react"
import { Loading } from "./Loading"

interface Props {
  article: Article
  isFetching: boolean
}
export const Article: React.FunctionComponent = ({ article, isFetching }) => {
  if(isFetching) {
    return <Loading />
  }

  return (
    <>
      <h1>{article.title}</h1>
      <div>{article.body}</div>
    </>
  )
}

isFetchingがtrueの間は、ローディング画面ができてfetchが終わりfalseになった時はちゃんと記事の表示ができたと思います。

しかし、例えば存在しない記事にきた場合、APIは404を返しますね。isFetchingはfalseになりますが、 article.titleundefined になります。

と、いった具合にAPIと連携することを想定すると気にする点が多数出てきます。
これがあるたびに initialState にその状態管理を行うstateをはやしていくのはとてもめんどくさい作業になると思います。

そこで、redux-api-structを使います。

redux-api-structを使い、簡単に記述する

redux-api-structは、状態。実データ。エラーを一つのstateで管理するようにするためのライブラリです。

initialStateの書き方を以下のようにしてみます。

import { ReduxAPIStruct } from "redux-api-struct"

interface InitialState {
  article: ReduxAPIStruct<Article>
}

こうすると、この article stateは以下のようなtreeを持つようになります。

article/
  - status
  - data
  - error

このstatusは INITIAL, FETCHING, SUCCESS, FAILUREの4つの状態をもつenumです。
dataに実際のresponseが入ります。
errorには、そのresponseのエラー内容がはいります。

これをコンポーネントで使う時は

import * as React from "react"
import { ReduxAPIState, ReduxAPIStruct } from "redux-api-struct"

interface Props {
  article: ReduxAPIStruct<Article>
}
export const Article = ({ article }) => {
  switch(article.status) {
    case ReduxAPIState.INITIAL:
    case ReduxAPIState.FETCHING:
      return <Loading />
    case ReduxAPIState.SUCCESS:
      return <ArticleTemplate article={article.data} />
    case ReduxAPIState.FAILURE:
      if(article.error.statusCode === 404) {
        return <p>page not found!</p>
      } else {
        return <p>error view</p>
      }
  }
}

これでAPIの状態別でviewを出すことができて、成功した場合にちゃんと記事詳細を表示することができ、エラーの場合のハンドリングまで行うことができます。

簡単にtypesafeにコンポーネントとデータを扱うことができるので、typescriptで書いているプロジェクトはぜひ。

その他、質問などがありましたら こちらにお願いします。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?