Vueを半年弱触ってた人がReactに移行して一ヶ月目の所感

# この記事の目的

Vue経験者でReactに興味ある人が"やるorやらない"を決めるキッカケになればいいかなと思います。

# 自己紹介

情報専門学生3年の[kazz](https://twitter.com/KzKzKazz)です。

学校とはまた別で、Web開発のデザイン以外を手広く触っています。最近はもっぱらSPAつくってます(バックNodejs,フロントReact,インフラAWS/EC2)。

ちなみに自分は、ReactもVueも個人制作やハッカソンで使う程度で実務では使ったことがないですし、そこまで精通しているわけでもないので、本記事は「そういうもんなんだな〜」程度で見てくれるとありがたいです。

# Reactに移行してみて

## 移行した経緯

一番は知的好奇心です。

正直、当時のVueの選択理由も学習コストの低さと情報の多さくらいで「まあVueでいいか」みたいな感じだったので、Vueを書き始めて3ヶ月ほどでやはりReactやAngulerに興味が湧いてきてしまい、JavaScriptもそこそこ書けるようになってきたのもあって、Reactに移行することにしました。

## Reactを選んだ理由

Angulerは大規模開発向きな分ニーズが偏っているそうで、現在の案件数も一番多く将来性もありそうなReactを選択しました。

それに、Angulerは小中規模のアプリを開発するにしてはオーバーヘッドが大きく向いていないそうですし、自分的には違うかなと感じました。

## VueからReactに移行して良かったこと、悪かったこと

カスタムフックなどで処理を外部化できたり、あらゆる面で拡張性の高さを感じました。一方で、理解が曖昧だとででたらめなコードを書いてしまいそうとだなとも感じました。

参考: [条件付きレンダー](https://ja.reactjs.org/docs/conditional-rendering.html)

実際Vueのときと比べ、Reactだから出来たとか出来ないだとかは特にありませんし、どちらも日本語ドキュメントが用意されていますし(※19年12月時点でReactは一部和訳されていない部分があるが、そんなに支障はなかった)、ライブラリも豊富に存在します。

## 難易度、学習コスト

現段階では、どちらもさほど変わらないかなという印象です。

ただ、VueのSFCのような直感的な分かりやすさはなく、少し慣れが必要かなと感じました。

あと、Vue以上にES6の理解は必要です。公式のチュートリアルにも「ES6 という JavaScript の最近のバージョンからいくつかの機能を使用していることにも注意してください」と書かれています。

最低でもconst,letや分割代入、スプレッド演算子、アロー関数くらいはやっておくとスムーズに学習できると思います。

# 実際に触ってみよう

## 環境構築(Vue環境有前提)

Nodejsの環境は出来上がっていると思うので、さくっとプロジェクトを作成しましょう。

まず、npmを使わずnpxコマンドからcreate-react-appを実行し、プロジェクトの雛形を作成します。

```
npx create-react-app sample-app
# TypeScriptを導入する場合は↓で実行
# npx create-react-app sample-app --typescript
```

こんな構成で出来上がると思います。
![Screenshot from 2019-12-19 03-19-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/ff3e3494-298c-43c8-459b-ccfa9a0fdf3e.png)

とりあえず`npm run start`でReactアプリケーションを起動してみましょう。
![Screenshot from 2019-12-19 03-34-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/e8e5c441-52bc-81ed-1682-5a7fb572a1ec.png)

さらに、ディレクトリを少し整頓して、src/App.jsを最小構成にしてみましょう。
![Screenshot from 2019-12-19 03-53-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/bb1f0213-d91a-3aff-7313-18d5e3c55d80.png)

結果
![Screenshot from 2019-12-19 03-54-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/74183312-9d47-edc0-0be4-5195f89d402e.png)

## Vueとの比較

せっかくなのでReactの一部機能をVueと比較しながら紹介します。

### Template→JSX

ReactではJSXという記法を使いHTMLを表現します。Vueでいう単一ファイルコンポーネントのtemplateみたいな感じです。

JSX構文を使えばHTMLをJavaScriptにそのまま書くことができ、更には変数に代入し利用することも可能です。View内で変数を使い場合は`{}`で囲めばその中は式として判定されます。

**Vueの場合**

```vuejs:App.vue
<template>
    <div class="App">
        Hello, OthloTech!
    </div>
</template>

<script>
export default { };
</script>

<style scoped>
.App {
    margin: 200px;
}
</style>
```

**Reactの場合**

```react:App.js
import React from 'react';
import "./App.css"; // <- CSSファイル適用

const App = () => {
    return (
        <div className="App">
            Hello, OthloTech!
        </div>
    );
}
export default App;
```

```css:App.css
.App {
    margin: 200px;
}
```

### State

VueでいうdataがReactのStateにあたります。コンポーネント内の情報で保持する変数です。

Vueはdataの中身を直接再代入すればViewにも反映されますが、Reactはそうはいきません。State定義時にあわせてセッター関数も定義し、そのセッター経由でデータの変更を行います。

次の例はよくあるカウントアップアプリを実装しています。

**Vueの場合**

```vuejs:App.vue
<template>
    <div>
        <div>{{count}}</div>
        <button @click="count += 1">PLUS</button>
    </div>
</template>

<script>
export default {
    data: () => ({
        count: 0
    })
};
</script>

```

**Reactの場合**

```react:App.js
import React from 'react';

const App = () => {
    //    value   setter                   初期値
    //      ↓        ↓                       ↓
    const [count, setCount] = React.useState(0);

    return (
        <div>
            <div>{count}</div>
            <button onClick={() => { setCount(count + 1) }}>PLUS</button>
        </div>
    );
}

export default App;
```

### Props

おなじみpropsです。

**Vueの場合**

```vuejs:SampleComponent.vue
<template>
    <div>
        <h2>{{title}}</h2>
        <div>{{content}}</div>
    </div>
</template>

<script>
export default {
    props: {
        title: String,
        content: String
    }
};
</script>
```

```vuejs:App.vue
<template>
    <div>
        <SampleComponent title="Title!1" content="Content!1" />
        <SampleComponent title="Title!2" content="Content!2" />
        <SampleComponent title="Title!3" content="Content!3" />
    </div>
</template>

<script>
import SampleComponent from "./components/SampleComponent.vue";

export default {
    data: () => ({
        count: 0
    }),
    components: {
        SampleComponent
    }
};
</script>
```

**Reactの場合**

```react:SampleComponent.js
import React from 'react';

const SampleComponent = (props) => {
    return (
        <div>
            <h2>{props.title}</h2>
            <div>{props.content}</div>
        </div>
    );
}

export default SampleComponent;
```

```react:App.js
import React from 'react';
import SampleComponent from './components/SampleComponent';

const App = () => {
    return (
        <div>
            <SampleComponent title="Title!1" content="Content!1" />
            <SampleComponent title="Title!2" content="Content!2" />
            <SampleComponent title="Title!3" content="Content!3" />
        </div>
    );
}

export default App;
```

### API通信(Axios)

こちらはVueは省略します。

useEffectはコンポーネントのレンダリング後に走らせたい処理をコールバックで渡します。その中でAxiosを使ってpokeAPIからデータを取得し、そのレスポンスをstateのセッター経由でviewに反映させます。

以下の例では[pokeAPI](https://pokeapi.co/)からポケモンの図鑑No1〜10のデータを取って、一覧表示させています。

**実行結果**

![Screenshot from 2019-12-19 23-59-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305522/33763032-562d-bfb3-ba80-b7c8a05de4c8.png)

**Reactコード**

```react:App.js
import React from 'react';
import Axios from "axios";

const App = () => {
    const [pokemons, setPokemons] = React.useState(null);
    const [isApiConFailed, setIsApiConFailed] = React.useState(false);

    React.useEffect(() => {
        Axios.get("https://pokeapi.co/api/v2/pokemon?offset=0&limit=10").then(response => {
            setIsApiConFailed(false);
            setPokemons(response.data.results);
        }).catch(error => {
            setIsApiConFailed(true);
        });
    }, []);

    const ApiResult = (
        pokemons !== null && (
            pokemons.map((pokemon, index) => (
                <div key={index}>
                    <h2>{pokemon.name}</h2>
                    <div>
                        <a href={pokemon.url}>このポケモンの詳細</a>
                    </div>
                </div>
            ))
        )
    );

    return (
        <div>
            {ApiResult}
            {isApiConFailed && <div>API通信エラー</div>}
        </div>
    );
}

export default App;
```

# 学習方法

１．ハローワールド
２．UdemyのReactコースや公式ドキュメントを参考に色んな機能を試す
３．Vueで作りかけだったものをReactで書き直す

React公式ドキュメント:
https://ja.reactjs.org/docs/getting-started.html