はじめに
この記事では、Vue の v-model と React の value + onChange を比較し、なぜ React の書き方が分かりやすいと感じやすいのかを整理します。
Vue を触ったことがある人なら、v-model の便利さはすぐ分かります。
一方で、React では同じことを少し冗長に書きます。
<input value={name} onChange={(e) => setName(e.target.value)} />
最初は Vue のほうが洗練されて見えます。
ただ、実務で長く読むコードとしては React のほうが追いやすいと感じる人もいます。
この記事では、その理由を「便利さ」と「追いやすさ」を分けて整理します。
先に結論
先に結論を書くと、次の違いです。
- Vue の
v-modelは短くて便利 - React の
value + onChangeは何をしているかが見えやすい
つまり、
- 書く速さでは Vue が強い
- 読んで追うときの明示性では React が強い場面がある
という話です。
Vueのv-modelは何を省略しているのか
Vue では次のように書けます。
<script setup lang="ts">
import { ref } from "vue";
const name = ref("名無し");
</script>
<template>
<input v-model="name">
<p>{{ name }}さんですね!</p>
</template>
これは短くて便利ですが、内部では次の2つをまとめてやっています。
- 入力欄に state を表示する
- 入力変更時に state を更新する
つまり、テキスト入力に対する Vue の v-model は、概念としては次の省略形です。
<script setup lang="ts">
import { ref } from "vue";
const name = ref("名無し");
const onInput = (event: Event): void => {
const element = event.target as HTMLInputElement;
name.value = element.value;
};
</script>
<template>
<input :value="name" @input="onInput">
<p>{{ name }}さんですね!</p>
</template>
さらに Vue は input の種類や modifier に応じて挙動も調整してくれます。
- text / textarea では主に
valueとinput - checkbox / radio では主に
checkedとchange - select では主に
valueとchange
このため、使う側から見るとかなり楽です。
Reactは省略せずに分けて書く
React では同じことを次のように書きます。
import { useState } from "react";
export function Sample() {
const [name, setName] = useState("名無し");
return <input value={name} onChange={(event) => setName(event.target.value)} />;
}
ここでは処理が分かれています。
-
value={name}で表示する値を決める -
onChange={...}で入力のたびに state を更新する
短くはありません。
ただ、何をしているかはかなり見えやすいです。
Vueでも明示的に書ける
ここは誤解しない方がよいです。
Vue は v-model を使うことが多いですが、必要なら明示的にも書けます。
<script setup lang="ts">
import { ref } from "vue";
const name = ref("名無し");
const onInput = (event: Event): void => {
const element = event.target as HTMLInputElement;
name.value = element.value;
};
</script>
<template>
<input :value="name" @input="onInput">
</template>
つまり、
- Vue は
v-modelで省略できる - 必要なら明示的にも書ける
- React は最初から明示的に書くのが基本
という違いです。
なぜReactのほうが分かりやすく感じやすいのか
React のほうが分かりやすいと感じる理由は、だいたい次の3つです。
- 表示と更新が分かれている
- イベント起点が明示されている
- 値変換の場所が見える
表示と更新が分かれている
React では、表示している値と更新処理が別々に書かれます。
<input value={name} onChange={(event) => setName(event.target.value)} />
このため、コードを読むときに次の問いへすぐ答えられます。
- 今どの state を表示しているか
- いつ state が変わるか
Vue の v-model はそこを短く包んでくれるので、慣れていれば速いです。
ただ、初見のコードで追うときは、React の明示的な分かれ方のほうが理解しやすい場面があります。
イベント起点が明示されている
React では onChange が見えるので、「どのイベントで state を更新するか」がコード上に出ます。
一方で Vue の v-model は便利なぶん、イベントの存在が後ろに下がります。
もちろん Vue を知っていれば v-model が何をしているかは分かります。
ただ、フォーム制御を細かく追うときは、イベントが見えている React の方が読みやすい場面があります。
値変換の場所が見える
React では、その場で変換処理を入れやすいです。
<input
value={ageText}
onChange={(event) => {
const value = event.target.value;
setAge(value === "" ? null : Number(value));
}}
/>
どこで文字列を数値に変えているかが見えます。
Vue にも .number のような modifier があります。
<script setup lang="ts">
import { ref } from "vue";
const age = ref(20);
</script>
<template>
<input v-model.number="age">
<p>{{ age }}</p>
</template>
これは便利です。
ただし、Vue の .number は単純に Number(...) するわけではありません。
数値として解釈できない入力は元の文字列のまま残りますし、空文字もそのまま残ります。
一方で React 側を Number(event.target.value) とだけ書くと、空文字が 0 になります。
そのため、React では空文字をどう扱うかまで含めて明示したほうが安全です。
そのうえで、React のように変換処理をそのまま書く形のほうが、JavaScript として素直に追えると感じる人は多いと思います。
Vueのほうが強い場面もある
ここは分けておいた方がよいです。
v-model は本当に便利です。
特に単純なフォームではかなり強いです。
- 記述量が少ない
- テンプレートが見やすい
- Vue の流儀に揃えやすい
つまり、Vue の v-model が悪いのではありません。
便利さを優先してよく整理された抽象化です。
Reactの書き方が効くのはどんな場面か
React の value + onChange が効くのは、次のような場面です。
- 入力値の変換が多い
- 更新条件が複雑
- フォームの追跡やデバッグをしたい
- 「どこで state が変わるか」を明確にしたい
このあたりは、実務コードで差が出やすい部分です。
まとめ
Vue の v-model と React の value + onChange は、どちらも同じ目的を持っています。
違いは、どこまでを短縮記法として吸収するかです。
- Vue の
v-modelは短くて便利 - React の
value + onChangeは明示的で追いやすい - 実務で React が分かりやすいと感じるなら、その理由は明示性にあることが多い
Vue は「よく使うフォーム処理をきれいに省略する」方向です。
React は「省略せず、JS とイベントとしてそのまま見せる」方向です。
そのため、便利さでは Vue が強く、明示性を重視すると React が追いやすい場面があります。