React+TypeScript+unstatedで始めるアプリ開発

# はじめに
**[React](https://ja.reactjs.org/)**は、Facebookとコミュニティによって開発されているユーザーインターフェース構築のためのJavaScriptライブラリです。大体の人は、JavaScriptでReactを書くと思いますが、**ReactとTypeScriptの相性が非常に良い**ので、ぜひTypeScriptでReactを始めてみてはいかがでしょうか？また、Reactで開発を進めていく中で、もっとも煩わしくなるのが状態(state)管理です。これを解消するために、ReduxやUnduxを使うのですが、正直難しいです笑。unstatedを使えば、比較的簡単に状態管理ができると聞いたので、今回は、[unstated](https://github.com/jamiebuilds/unstated)を使ってみたいと思います。

# 開発環境の構築

### **nodeのインストール**
[こちらのページ](https://nodejs.org/ja/)から、推奨版をインストールしてください。macの人は**nodebrew**、Windowsの人は**nodist**を使うと、nodeのバージョンを簡単に切り替えることができるので、そっちを使ったほうがいいかもしれないです。


### **create-react-appコマンドのインストール**
ターミナル、またはコマンドプロンプトを開いて、以下のコマンドを打ち込んでください。

```
$ npm install -g create-react-app
```

### **アプリの雛形を作る**

create-react-appコマンドを使えば一発でアプリの雛形ができる。オプションで**--template typescript**をつけることで、TypeScriptを導入できる。

```
$ create-react-app my-app --template typescript
```

### **サーバーを開始する**

cdコマンドで、先ほど作ったアプリに移動し、**npm start**で起動する。

```
$ cd my-app
$ npm start
```

http://localhost:3000/ にアクセスして、このような画面が出てきたら成功。

<img width="1386" alt="スクリーンショット 2019-12-29 10.30.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/229a7141-4b25-81e4-149b-bc9cf3c12472.png">

# カウントアプリを作る
今回は、簡単なカウントアプリを作ってみたいと思います。

### コンポーネント（部品）作り

今回は、カウントを表示するコンポーネント(Display.tsx)とカウントを増やすボタン(Button.tsx)の二つを作りたいと思います。src直下に、componentsフォルダを作り、その中に**Display.tsx**と**Button.tsx**を作ります。

```
src-
   |-components
         |-Display.tsx
         |-Button.tsx
```

```Display.tsx
import React from 'react'

const Display: React.SFC = () => {
    return (
        <div>
            <h1>0</h1>
        </div>
    )
}

export default Display
```

```Button.tsx
import React from 'react'

const Button: React.SFC = () => {
    return (
        <div>
            <button>add!</button>
        </div>
    )
}

export default Button
```

```App.tsx
import React from 'react';
import Display from './components/Display';
import Button from './components/Button';

const App: React.FC = () => {
  return (
    <div>
      <Display />
      <Button />
    </div>
  );
}

export default App;
```

### unstatedの導入

**unstatedは、コンポーネントの状態(state)の部分と見た目の部分を引き剥がすだけです。**なので、簡単に導入できます。
従来のコンポーネントは、一つのファイルで、状態(state)の管理と見た目の作成を全てを担っていました。

<img width="405" alt="スクリーンショット 2019-12-29 11.11.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/a1ba318e-b4fb-4690-64cc-3da538302169.png">

これだと、状態を他のコンポーネントに再利用できなくて、どうしても他のコンポーネントに状態を渡したいときは、**props経由で状態(state)を渡します**。ただ、コンポーネントの構造が複雑になってくると、propsで状態を渡すのがだるくなってきます。しかも、propsは親から子には渡せますが、子から親には渡せないので、開発を進めていると途中で詰むことがあります。

<img width="706" alt="スクリーンショット 2019-12-29 11.20.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/3a474b31-85be-c80f-1f07-28dcb03144c5.png">

一方で、unstatedは、見た目の部分と状態の部分に分けます。状態の部分を、**コンテナ**と呼びます。

<img width="923" alt="スクリーンショット 2019-12-29 11.30.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/cb318405-a132-d38d-7d45-4125ca5523d1.png">

unstatedは、親子関係なしに状態（state）を渡すことができます。

<img width="878" alt="スクリーンショット 2019-12-29 11.27.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/58c9f72a-746b-97e3-8827-806324c2cd80.png">

では、実際にunstatedを導入してみます。

```
$ npm install unstated
$ npm install
```

unstatedを使うためには、**Providerで囲む必要があります**。

```App.tsx
import React from 'react'
import Display from './components/Display'
import Button from './components/Button'
import { Provider } from 'unstated'

const App: React.FC = () => {
  return (
    <Provider>
      <div>
        <Display />
        <Button />
      </div>
    </Provider>
  );
}

export default App;
```

containerを作ります。src直下にcontainersフォルダを作り、その中にCountContainer.tsを作ります。

```
src-
   |-containers
   |     |-CountContainer.ts
   |
   |-components
         |-Display.tsx
         |-Button.tsx
```

```CountContainer.ts
import { Container } from 'unstated'

interface IState {
    count: number
}

export default class CountContainer extends Container<IState> {
    public constructor() {
        super()
        this.state = {
            count: 0
        }
        this.addCount = this.addCount.bind(this)
    }

    public addCount() {
        this.setState({
            count: this.state.count + 1
        })
    }
}
```

containerにあるstateをDisplay.tsxに渡します。

```Display.tsx
import React from 'react'
import { Subscribe } from 'unstated'
import CountContainer from '../containers/CountContainer';

const Display: React.SFC = () => (
    <Subscribe to={[CountContainer]}>
        {(cCount: CountContainer) => {
            return (
                <div>
                    <h1>{cCount.state.count}</h1>
                </div>
            )
        }}
    </Subscribe>
)

export default Display
```

containerにあるaddCountという関数をButton.tsxに渡します。

```Button.tsx
import React from 'react'
import { Subscribe } from 'unstated'
import CountContainer from '../containers/CountContainer';

const Button: React.SFC = () => (
    <Subscribe to={[CountContainer]}>
        {(cCount: CountContainer) => {
            return (
                <div>
                    <button onClick={cCount.addCount}>add!</button>
                </div>
            )
        }}
    </Subscribe>
)

export default Button
```

addボタンを押すと、カウントが増えていきます。

![react.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/91880550-ea67-f847-a042-f239d332d0ad.gif)