Reactでローディング中のデータを表現する方法の案

## 概要

レスポンスデータとloadingのステートをセットにして表現すると良さそうではないですか！という提案の記事です


```ts
// typescriptです

enum Status {
  loading = 'loading',
  success = 'success',
  failure = 'failure',
}

interface IResult<T> {
  status: Status
  response: T | Error | null
}

interface IHit {
  objectID: string
  url: string
  title: string
}

interface IState {
  hits: IResult<IHit[]>
}

class App extends Component<{}, IState> {

  componentDidMount() {
    this.setState({
      hits: {
        status: Status.loading,
        hits: null
      }
    });

    fetch(API + DEFAULT_QUERY)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Something went wrong ...');
        }
      })
      .then(data => this.setState({ hits: data.hits, isLoading: false }))
      .catch(error => this.setState({ hits: error, isLoading: false }));
  }

  render() {
    const { hits } = this.state;

    if (hits.status === Status.failure) {
      return <p>{hits.response.message}</p>;
    }

    if (hits.status === Status.loading) {
      return <p>Loading ...</p>;
    }

    return (
      <ul>
        {hits.response.map(hit =>
          <li key={hit.objectID}>
            <a href={hit.url}>{hit.title}</a>
          </li>
        )}
      </ul>
    );
  }
}
```


## 背景

普通は下記のような方法で表現することが多いかなと思います

参考: https://medium.com/@ghengeveld/async-data-loading-in-react-94661e23cd3d

```js
class App extends Component {

  componentDidMount() {
    this.setState({ isLoading: true });

    fetch(API + DEFAULT_QUERY)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Something went wrong ...');
        }
      })
      .then(data => this.setState({ hits: data.hits, isLoading: false }))
      .catch(error => this.setState({ error, isLoading: false }));
  }

  render() {
    const { hits, isLoading, error } = this.state;

    if (error) {
      return <p>{error.message}</p>;
    }

    if (isLoading) {
      return <p>Loading ...</p>;
    }

    return (
      <ul>
        {hits.map(hit =>
          <li key={hit.objectID}>
            <a href={hit.url}>{hit.title}</a>
          </li>
        )}
      </ul>
    );
  }
}
```

これだけ見るとあんまり違いはないのですが、fetchするデータの種類が増えてくると `isLoadingHits`, `isLoadingUsers` みたいに管理すべきフラグが増えて複雑になってしまうなと感じていました

そこで私は下記のような書き方することもあったのですが、わかりにくいしあんまりよくないなと感じていました

```ts
interface State {
  // undefined: 未取得
  // null: 取得失敗したとき
  // IHit[]: 取得成功したとき
  hits: IHit[] | undefined | null
}
```


提案している`IResult`はswiftを真似ています

https://github.com/ishkawa/APIKit#apikit

```swift
// SearchRepositoriesRequest conforms to Request protocol.
let request = SearchRepositoriesRequest(query: "swift")

// Session receives an instance of a type that conforms to Request.
Session.send(request) { result in
    switch result {
    case .success(let response):
        // Type of `response` is `[Repository]`,
        // which is inferred from `SearchRepositoriesRequest`.
        print(response)

    case .failure(let error):
        self.printError(error)
    }
}
```

swiftだと↑こんな感じでレスポンスの結果をenumで表現する文化があるのですが、これが結構わかりやすくて好きで、同じ事をやりたいなというモチベーションです

typescriptの型システム的に値付きenumっぽい表現ができなさそうだったので、ちょっと妥協して今回のような型になっています
(本当はこういう風に表現したかった)

## redux

この例ではreactのstateにデータを保持していますが、reduxのstoreでも全く同じ要領でやれるかなと思っています


## 今後

個人のPJで試しにやってみているので、コードが公開できるようになったら、実際にどんな感じか感想を書こうと思います