【5分でわかる】React + TypeScript + unstated

# はじめに

最近ハッカソンに参加した際に採用したフロントの技術が個人的にかなりしっくりきたので紹介したいと思います． GitHubのリポジトリ一覧を取得するAPIを用いて説明を行います．

【！**注意**！】 本記事はReactの記法やunstatedの使い方について詳しく説明を行う記事ではなく，あくまでこうした技術を用いてコーディングするとやりやすかったですというのを紹介する記事となっています．

# プロジェクト作成

今回はcreate-react-appを用いてプロジェクト作成を行います．create-react-appを用いる場合，オプションをつけるだけで簡単にTypeScriptバージョンのプロジェクトを作成できます．

```
> create-react-app --scripts-version=react-scripts-ts {プロジェクト名}
```

次に `unstated` の導入を行います．また，API通信を行うときに用いる`axios` も同時に導入しておきます．

```
> yarn add unstated
> yarn add axios @types/axios
```

続いて，作成したプロジェクトのディレクトリの中の`src`ディレクトリ配下に`components` と `store` というディレクトリを作成します．

これで前準備は完了です．


# 実装

今回はGitHubのユーザー名を指定して，そのユーザー名のリポジトリ一覧を取得する簡単なアプリケーションを実装していきます．   

はじめに,unstatedを用いたstoreについての実装から行います．こちらは`store/githubContainer.tsx`として実装を行なっていきます．
コードは以下のようになります．

```tsx:store/githubContainer.tsx

import { Container } from "unstated";
import axios from "axios";

interface Repository {
  name: string;
}

interface GitHubState {
  userName: string;
  repositories: Repository[];
  err: string;
}

export class GitHubContainer extends Container<GitHubState> {
  constructor() {
    super();
    this.state = {
      userName: "",
      repositories: [],
      err: ""
    };
  }

  setUserName(input: string) {
    this.setState({ userName: input });
  }

  fetch(userName: string) {
    axios
      .get("https://api.github.com/users/" + userName + "/repos")
      .then(res => {
        this.setState({ repositories: res.data });
      })
      .then(err => {
        console.log(err);
        this.setState({ err: "Fetch Repository Error" });
      });
  }
}

```

unstatedが本来Viewファイル(今回でいうと`components/`に入るようなファイル)に入るところのstateを別ファイルに切り出してくれているという風に考えるとわかりやすいかと思います．今回はGitHubのリポジトリを取得するので， `Repository` というinterfceを作成してそのリストをstateに持たせるようにしました．こうした実装ができるところがTypeScriptのいいところで，JSに比べてかなり可読性が高く直感的なコードが実現できています．

次に`components/repositories.tsx` にViewを実装します．コードは以下のようになります．

```tsx:components/repositories.tsx
import * as React from "react";
import { Subscribe } from "unstated";
import { GitHubContainer } from "../store/githubContainer";

interface RepositoriesProps {
  container: GitHubContainer;
}

export class RepositoriesContainer extends React.Component<
  RepositoriesProps,
  {}
> {
  constructor(props: RepositoriesProps) {
    super(props);
  }

  render() {
    return (
      <div>
        <div>
          <input
            placeholder="Input GitHub User Name..."
            onChange={e => this.props.container.setUserName(e.target.value)}
          />
          <button
            onClick={() =>
              this.props.container.fetch(this.props.container.state.userName)
            }
          >
            GET!
          </button>
        </div>
        <ul>
          {this.props.container.state.repositories.map(repository => (
            <p> {repository.name} </p>
          ))}
        </ul>
      </div>
    );
  }
}

const RepositoriesWrapper = () => (
  <Subscribe to={[GitHubContainer]}>
    {(container: GitHubContainer) => (
      <RepositoriesContainer container={container} />
    )}
  </Subscribe>
);

export default RepositoriesWrapper;
```

ここで肝となっているのは， `RepositoriesWrapper` の部分で，本来unstatedのstoreを用いるには少々面倒な書き方を行う必要があるのですが([unstated公式ページ参照](https://github.com/jamiebuilds/unstated))，そのstoreをあらかじめpropsに設定しておくことでどこからでも簡単に呼び出すことを可能にしている点です．こうすることでstoreを簡単に用いることができるだけではなくunstatedを普通に用いた場合のなんとも言えぬゴチャゴチャ感を感じずに実装を行うことができます． 
ここら辺の技術は[この記事](https://qiita.com/kaba/items/b05f680f850dd46548f3)を参考にさせていただきました．

あとは`App.tsx`と`index.tsx`に修正を加えてアプリが動作するようにします．

```tsx:App.tsx
import * as React from "react";
import "./App.css";
import RepositoriesWrapper from "./components/repositories";

class App extends React.Component {
  public render() {
    return (
      <div className="App">
        <RepositoriesWrapper />
      </div>
    );
  }
}

export default App;
```

```tsx:index.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "unstated";
import "./index.css";

ReactDOM.render(
  <Provider>
    <App />
  </Provider>,
  document.getElementById("root") as HTMLElement
);
```

最後にアプリを実行して，きちんと動作するか確認してみてください．

# さいごに

今までJSを用いてReactでの開発を行なっていたのですが，今回の技術セットを用いた開発がかなり良いと感じたので紹介しました．よほど大規模なプロジェクトでない場合，しばらくはこの技術セットを用いてReactの開発を行なっていこうと思います．