【逆引きリファレンス】Vue と React の書き方 比較とまとめ

# はじめに

実務では Vue / Nuxt をメインにフロントエンド 開発をしています。この度 React の案件にアサインされることをきっかけに React の学習をはじめたのですが、

- Vue でやってたことって React やとどうやってやるんや？？
- Vue / React の対象表みたいなのはいくつかあるけど、Vue の Composition API や React の関数コンポーネント、フックスに対応した記事がない…

ということでこの度まとめてみました。

## 読んだ方がええ人

- 普段は Vue を使用していて最近 React をはじめた人（逆も然り）
- 普段は Vue を使用しているが、 React にも興味がある人（逆も然り）

## 読む前に知っておいてほしいこと

- Vue は **Composition API** 、React は**関数コンポーネント**や **Hooks** を用いることを前提として記述しています
- 多分記載している以外にもやり方はありますが、一番主流**であろう**書き方を取り上げています。
- 1つ1つの機能に関して掘り下げた内容は展開しません
- 各項目の最後に「個人的に分かりやすいと思ったサイト」のリンクを載せておきますので、気になる方はそちらをご参照いただけると幸いです

# コンポーネントの作成

## Vue では vue ファイル

- Single File Component （通称SFC） を用いるのが主流
- HTML, JS, CSSを１ファイルにまとめて書ける

```vue
<template>
  <div>
    <p class="text">
      {{ text }}
    </p>
  </div>
</template>

<script>
import { defineComponent, ref } from '@nuxtjs/composition-api'
export default defineComponent({
  setup() {
    const text = ref('TEXT')
    // template 内で使用する変数や関数は return の中に記述する
    return { text }
  },
})
</script>

<style lang="scss">
.text {
  color: red;
}
</style>

```
- setUp の中で JS 式を記述する
- 使用する関数や変数は return の中に書く
- 後述しますが、HTML 内部で使う変数は ref を用いる
- マスタッシュ構文 `{{ }}` を使えばその中に JS 式を記述できる

https://v3.ja.vuejs.org/api/sfc-spec.html

## React は jsx or tsx ファイル

```jsx
import { useState } from 'react'

const Component = () => {
  const [text, setText] = useState('')
  return (
    <div>
      <p className="text">{text}</p>
    </div>
  )
}

export default Component
```
- JSX は JavaScript を拡張して、UI 要素を記述するのに HTML のようなタグ構文が使えるようにしたもの
- Typescript を使用する場合は tsx を使用する
- 使用する DOM は return の中に記述する
- JSX の式は普通の JavaScript の関数呼び出しに変換され、JavaScript オブジェクトへと評価されます
- JSX を if 文や for ループの中で使用したり、変数に代入したり、引数として受け取ったり、関数から返したりすることができる
- CSS の書き方は CSS in JS や CSS Modules などの選択肢がある


https://ja.reactjs.org/docs/introducing-jsx.html

CSS の選択肢

https://zenn.dev/irico/articles/d0b2d8160d8e63

# イベント処理

## Vue では v-on ディレクティブを使う

```vue
 <button v-on:click="change()">変更する</button>
```
- `v-on:click` は `@click` と短縮形で記載することもできる
```vue
<button v-on:click="change()">変更する</button>
```

- 主要イベントの書き方は以下サイトがわかりやすかったです

https://blog.capilano-fw.com/?p=2787#i-2

## React では イベントハンドラとして関数を渡す

```jsx
<button onClick={() => change()}>
  変更する
</button>
```

`onClick={change()}`のように書いてしまうと、初回レンダリング時にもイベントが発火してしまうので注意してください。詳しくは以下サイトをご覧ください。

https://qiita.com/jima-r20/items/839da7c2f26366491298

https://ja.reactjs.org/docs/handling-events.html



# 値の保持、変更

## Vue では ref を使う

```html
<div>
  <p class="text">
    {{ text }}
  </p>
  <button @click="changeText">変更する</button>
</div>
```

```js
  setup() {
    const text = ref('TEXT')
    const changeText = () => {
      text.value = 'HOGE'
    }
    return { text, changeText }
  },
```

- HTML内で使う変数は ref で囲む
- ref で囲まれた変数を参照、更新するときは `.value` をつける

## React では useState を使う

```tsx
const [text, setText] = useState('TEXT');
  const changeText = () => {
    setText('HOGE');
  };
  return (
    <div>
      <p>{text}</p>
      <button onClick={() => changeText()}>変更する</button>
    </div>
  );
```

- `const [値, 値を更新する関数] = useState(値の初期値)` の形で書ける
- 値の更新は第二引数で指定した「値を更新する関数」でのみ変更できる

ちなみに オブジェクトだとスプレッド構文で書ける

```tsx
const [countObj, setObj] = useState({
　　countA: 0,
　　countB: 0,
});

const countAup = () => {
　　setObj({ ...countObj, countA: countObj.countA + 1 });
};

const countBup = () => {
　　setObj({ ...countObj, countB: countObj.countB + 1 });
};
```

# input 要素に入力した値を保持、変更

## Vue では v-model を使う

```vue
<div>
　<p>{{ text }}</p>
  <input v-model="text" type="text" />
</div>    

```

- v-modelはv-onとv-bindをまとめて一行で書くためのシュガーシンタックス

以下記事がわかりやすかったです。
https://qiita.com/simezi9/items/c27d69f17d2d08722b3a#v-model%E3%81%AE%E5%8B%95%E4%BD%9C

## React では useState を使う

```tsx
const [text, setText] = useState('')
  return (
    <div className="App">
      <p>{text}</p>
      <input value={text} onChange={(e) => setText(e.target.value)} type="text" />
    </div>
  )
```

# 条件によって表示を出し分ける

## Vue では v-if もしくは v-show を使う

```vue
<p v-if="isDisplay">true なので表示します！</p>
<p v-else>false なので非表示です…</p>

もしくは、、

<p v-show="isDisplay">true なので表示します！</p>
<p v-else>false なので非表示です…</p>
```
```ts
setup() {
    const isDisplay = ref(true)
    return {
      isDisplay,
    }
  },
```

具体的な説明はこちら https://qiita.com/aqua_ix/items/61eac355f3c24d7676e1

## React 

### 三項演算子

```tsx
 const [isShow, setShow] = useState(false)
  return (
    <div className="App">
      {isShow ? <p>true なので表示します！</p> : <p>false なので非表示です…</p>}
    </div>
  )
```

- 条件が `false` の時に表示させるものがある場合に最適です

### &&演算子

```tsx
const [isShow, setShow] = useState(true)
  return (
    <div className="App">
      {isShow && <p>true なので表示します！</p>}
    </div>
  )
```

- `true` の時に表示され、`false`時に表示するものがない場合に最適

### 早期リターン

```tsx
const [isShow, setShow] = useState(false)
  if (isShow) {
    return <p>true なので表示します！</p>
  }
  return <p>false なので非表示です…</p>
```

- 表示する DOM が増えてしまうと三項演算子で書いた時に見辛くなるので、早期リターンがオススメ

他にもいろいろ書き方あるのでよければご覧ください。
https://nishinatoshiharu.com/conditional-rendering/

# 配列の中身を表示する


```js
// 表示したい配列
// ※ Vue の場合 ref や computed で囲みます
const family = [
  {
    name: 'たかし',
    role: '父',
    age: 50,
  },
  {
    name: 'たか子',
    role: '母',
    age: 48,
  },
  {
    name: 'ひろと',
    role: '長男',
    age: 18,
  },
  {
    name: 'まい',
    role: '長女',
    age: 15,
  },
]
```

## Vue では v-for を使う

```vue
<div v-for="(person, index) in family" :key="index">
  <p>{{ person.name }}</p>
  <p>{{ person.role }}</p>
  <p>{{ person.age }}</p>
</div>
```

## React では map を使う

```tsx
return (
    <>
      {family.map((person) => {
        return (
          <div>
            <p>{person.name}</p>
            <p>{person.role}</p>
            <p>{person.age}</p>
          </div>
        );
      })}
    </>
  );
```

- ループ処理の書き方は他にも色々あるので、気になる方は以下サイトをご覧ください！

https://nishinatoshiharu.com/jsx-map-roop/

# 初期描画時に処理を行う

## Vue では onMounted や onBeforeMount を使う

```ts
onBeforeMount(() => {
  alert('DOMがマウントされる前です')
})

onMounted(() => {
  alert('DOMがマウントされました')
})
```

初回読み込み時にAPIを叩いて値を入れたりすることもできる

```ts
const users = ref()
onMounted(async () => {
  users.value = await $axios.get('api/users')
})

```

- `onMounted` は DOMがマウントされたとき、`onBeforeMount` は DOM が描画される前に発火する
- 詳しくは Vue のライフサイクルフックをご覧ください

https://v3.ja.vuejs.org/guide/composition-api-lifecycle-hooks.html

## React では useEffect を使う

- 第2引数に空配列を渡すことで初回だけ発火する

```tsx
 useEffect(() => {
  alert('発火');
 }, []); // 第2引数に空配列を渡す
```

非同期で async await 使う時は以下のように書く

```tsx
　const [users, setUsers] = useState();
  useEffect(() => {
    (async () => {
      const response = await axios.get('api/users');
      setUsers(response.data);
    })();
  }, []);
```

https://zenn.dev/syu/articles/b97fb155137d1f

https://dev.classmethod.jp/articles/react-hooks-use-effect-infinity-loop/

# 値の変更を検知したい

## Vue では watch を使う

```ts
watch(監査対象の値, () => {
  // 発火させたい処理を書く
}) 
```

```ts
const text = ref('text')
watch(text, () => {
    console.log('textが変更されました')
})    
```
- オブジェクトのプロパティや配列の内部を監視したい時は書き方を工夫する必要があります。
- 以下のサイトに書き方が分かりやすく纏まっていて分かりやすかったです。

https://www.netlify.com/blog/2021/01/29/deep-dive-into-the-vue-composition-apis-watch-method/

## React では useEffect を使う

```tsx
const [text, setText] = useState('');
useEffect(() => {
  console.log('textが変更されました');
}, [text]); // 第2引数の配列の中の値に監査対象の値を含める
```

- 第2引数の配列の中の値に監査対象の値を含める
- ⚠️ 初回読み込み時も処理が走ってしまうので注意

# 値を「算出」したい

- 例えば countA と countB の合計値を算出したいとする

## Vue では computed を使う
```ts
const countA = ref(0)
const countB = ref(0)

const total = computed(() => {
  return countA.value + countB.value
})

//　アロー関数と同じで return を省略もできる
const total = computed(() => countA.value + countB.value)
```

- 値を算出する場合は `computed`プロパティ を使う
- 以下のような ref を使った書き方は値の変更を検知してくれないので注意

```ts
// countA と countB の値が変わったときに変更を検知してくれない
const total = ref(countA.value + countB.value)
```

## React だと変数に直接代入するだけで OK

```tsx
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const total = countA + countB;
```
- countA、countB に変更があったときも勝手に更新される
- React だと値が変わるたびに全体がレンダリングされるから（だと勝手に思ってる）

# 最後に

普段は Vue を使ってるのですが、 「〇〇したい時に React だとどうやって書くんだろう？」 と思うことが多々ありました。そんな時にチートシートがあれば毎回調べなくてもいいと思いこの度まとめてみました。普段使っていない方のフレームワークを学び始めたとにのおともにこの記事を参考にしていただければ嬉しい限りです。