これまでVueを使ってきましたが、最近Reactを学び始めました。
その中で気になったのは、「状態管理(useState)」と「ライフサイクル(useEffect)」の違いです。
Vueも、Vue 2とVue 3では書き方がかなり変わりました。
Vue 3で導入されたComposition APIは、Reactの記述スタイルに近くなったと言われていますが、実際に触ってみると、やはり違いも多くあると感じました。
本記事では、Vue 3(Composition API)とReactの使い方の違いや、初めてReactを触った際に戸惑った点をまとめてみます。
状態管理の違い:ref vs useState
ボタンのクリック回数を表示するUIを、Vue 3 と React でそれぞれ作ってみました。
Vue ではこうだった
<script setup lang="ts"> import { ref } from 'vue' const count = ref(0) const increment = () => { count.value++ } </script> <template> <button @click="increment">{{ count }}回クリックしました</button> </template>
- ref(0) で状態を作り、count.value で値を読み書きしていた。
- テンプレート内では count とだけ書けばよくて .value は不要。
- 状態の値を直接書き換えるイメージで操作していた。
React ではこうだった
import { useState } from 'react' export default function Counter() { const [count, setCount] = useState<number>(0) return ( <button onClick={() => setCount(count + 1)}> {count}回クリックしました </button> ) }
- useState は配列を返していて、分割代入で [count, setCount] と書く必要がある。
- 状態を更新するには setCount という関数を必ず使う。
- 直接 count に代入しても状態は変わらず、必ず更新関数を呼ぶ必要がある。
感じた違い
Vue は状態を箱(ref)で包んで直接値を書き換えるイメージなのに対して、
React は状態の値と更新関数がセットで返ってきて、更新は関数を通して行うので、
「状態変数と更新関数を同時に扱う」という点が初めてだとけっこう新鮮でした。
ライフサイクルの違い:onMounted / watch vs useEffect
以下は、「コンポーネントがマウントされたときにログを出す」「状態が変わったときに別の値を更新する」処理を、Vue 3 と React で書いた例です。
Vue ではこうだった
<script setup lang="ts"> import { ref, onMounted, watch } from 'vue' const count = ref(0) const doubled = ref(0) onMounted(() => { console.log('コンポーネントがマウントされました') }) watch(count, (newVal) => { doubled.value = newVal * 2 }) </script> <template> <button @click="count++">クリック: {{ count }}</button> <p>2倍: {{ doubled }}</p> </template>
- onMounted でコンポーネントのマウント時の処理を書いていた。
- watch で特定の状態の変化を監視してリアクティブに反応できた。
- ライフサイクルが細かく分かれていて、それぞれの役割が明確だった。
※本来は doubled のような派生値は computed を使うのが自然ですが、今回は React の useEffect に近い書き方を比較するために watch を使っています。
React ではこうだった
import { useState, useEffect } from 'react' export default function Counter() { const [count, setCount] = useState(0) const [doubled, setDoubled] = useState(0) useEffect(() => { console.log('コンポーネントがマウントされました') }, []) useEffect(() => { setDoubled(count * 2) }, [count]) return ( <> <button onClick={() => setCount(count + 1)}>クリック: {count}</button> <p>2倍: {doubled}</p> </> ) }
- すべての副作用は useEffect で書く。
- 第2引数の依存配列によって発火タイミングを制御する。
- 依存配列が空 [] のときは、Vueの onMounted と同じくマウント時に一度だけ実行される。
- 依存配列に値を入れると、Vueの watch と同じくその値が変わるたびに実行される。
- 一つのフックで複数の役割を兼ねるため、最初は慣れが必要。
感じた違い
Vue は「変数を定義し、その変数の変化に対して何かする」というスタイルで、たとえば watch を使って「この値が変わったらこの処理をする」といった書き方になります。
一方で React の useEffect は「この処理を実行したい、ただしこの値が変わったときにだけ」というように、処理を主体にして依存関係を後から指定する、という順序の違いを感じました。
つまり、Vue は「値 → 処理」の流れ、React は「処理 → 値」の流れで考える必要があり、この考え方の違いに最初は戸惑いました。
まとめ
Vue と React にはそれぞれ特徴があり、状態管理やライフサイクルの扱い方も異なります。
Vue はライフサイクルが細かく分かれていて直感的に使いやすく、
React はフックを使って一つの仕組みで柔軟に管理できる反面、慣れるまで少し時間がかかります。
私自身もまだReactを学習中の身なので、今回の違いを踏まえてさらに理解を深め、
より良いコードを書けるように引き続き勉強していきたいと思います。