🎁

VueとReactのpropsの違い

2025/02/18に公開2

最近はReact系フレームワークを使用しているユースケースがとても増えていますが、Vue系フレームワークもまだまだシェアを誇っています。
その中で、両者の違いはあるとはいえ、データの受け渡しはpropsだよね、ということで、VueとReactのpropsについて振り返りがてらまとめてみるのもいいのかなと思い、やっていこうと思います。

宣言の可否

Vue.jsの場合

公式ドキュメントからの引用にはなりますが、Vue.jsでpropsを使用する際は、明示的な宣言が必要になります。

  • Composition API での宣言
<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>
  • Options APIでの宣言
export default {
  props: {
    foo: {
      type: String, // 期待する型
      default: 'bar' // デフォルト値を指定
    }
  },
  setup(props) {
    console.log(props.foo)
  }
}

Vue.jsの特徴としては、

  • 意図しないpropsの使用を防げる
  • コンポーネントの入力値が明確
  • 型チェックとバリデーションが標準で可能

といったところでしょうか。

Reactの場合

Reactでは宣言をしなくても、すぐに使用することができます。例えば、

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

<Welcome name="Hoge" />

分割代入を使用すると、

function Welcome({ name, age }) {
  return <h1>Hello, {name}! You are {age} years old.</h1>;
}

<Welcome name="Fuga" age={25} />

JavaScriptだとPropTypesなどで必要に応じて型チェックを行うことができますが、TypeScriptを扱うケースも増えているので、型チェックはそちらで行うことが多くなっているように思います。
(というのも、ReactのPropTypesはReact 19より非推奨になったので、TypeScriptを使う方が増えていくでしょう)
Reactの特徴としては、

  • 開発の初期段階で素早くプロトタイピングが可能
  • コード量が少なく、シンプルに書ける
  • 柔軟なpropsの受け渡しが可能

といった感じになりますね。

Propsの不変性とローカルステートへの展開

両フレームワークとも、propsは基本的に不変(immutable) として扱われます。
直接変更すると、データの整合性が崩れてしまう可能性があるため、変更が必要な場合はローカルな状態にコピーして扱うのが定石です。

Vue.jsの場合

  • リアクティブだが変更禁止

Vueではpropsはリアクティブですが、子コンポーネント内で直接変更するのは避けるべきです。もしユーザーの入力などで値を変更する必要があるなら、ref や reactive でローカル状態を用意します。

<script setup>
const props = defineProps(['initialValue'])
// propsを直接変更せず、ローカルな状態として管理する
const localValue = ref(props.initialValue)
</script>

<template>
  <input v-model="localValue" />
</template>

Reactの場合

  • propsは読み取り専用

Reactではpropsは常に読み取り専用です。値を変更する必要がある場合は、useStateなどでローカル状態に展開するパターンが一般的です。

function Counter({ initialValue }) {
  const [count, setCount] = useState(initialValue);

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

パフォーマンスとレンダリング最適化

Reactの場合

親コンポーネントから渡されるpropsが頻繁に変わると、子コンポーネントも再レンダリングされてしまいます。
そのため、React.memoやuseMemo、useCallbackなどのHooksを活用して、不要なレンダリングを防ぐ工夫が重要です。

const MemoizedComponent = React.memo(function MyComponent({ data }) {
  // dataが同じなら再レンダリングされない
  return <div>{data}</div>;
});

Vue.jsの場合

Vueのリアクティブシステムは、依存関係を細かく追跡するので、実際に参照している部分だけが更新されます。
ただし、大量のデータや複雑なオブジェクトをpropsとして渡す場合は、パフォーマンス面で注意が必要な場合もあります。

コンポーネント設計におけるpropsの役割

propsは、コンポーネント同士の「契約書」としての役割を担っています。

  • インターフェースの明確化
    • どんなデータが必要か、またはどんな形式のデータが渡されるかを明示することで、コンポーネントの再利用性や保守性が向上します。
  • 疎結合な設計
    • 親コンポーネントと子コンポーネントが明確に分離され、データフローが一方向(親→子)になることで、予期せぬ副作用を防止できます。
  • 型チェックによる安全性
    • Vue.jsは宣言時に型やバリデーションを組み込める一方、ReactではPropTypesやTypeScriptを使って型の安全性を確保する方法があるので、どちらもコードの堅牢性を高められます。

まとめ

ここまで、Vue.jsとReact.jsにおけるpropsの取り扱いについて簡単に振り返ってきました。
ポイントは以下の通りです。

  • 宣言の必須性
    • Vue.jsは明示的な宣言が必須で、型チェック・バリデーションが内蔵されている
    • Reactは宣言不要で柔軟に使用できるが、型チェックはPropTypesやTypeScriptで補完する
  • 不変性の扱い
    • propsは基本的に不変。変更が必要な場合はローカル状態に展開するのが一般的。
  • パフォーマンス最適化
    • 特にReactでは、再レンダリングを抑えるための工夫(React.memoやuseMemoなど)が重要。
    • Vueは依存関係のトラッキングが優秀だが、大規模データの場合は注意が必要。
  • コンポーネント設計の基礎
    • propsはコンポーネントのインターフェースそのものであり、保守性と再利用性を向上させてくれる。

参考文献

https://ja.vuejs.org/guide/components/props#reactive-props-destructure
https://ja.react.dev/learn/passing-props-to-a-component

ラブグラフのエンジニアブログ

Discussion

あいや - aiya000あいや - aiya000

まさしく蛇足ですが、Vueで「このpropsは子コンポーネント側で変更して欲しい(変更を許可する)」という特別なpropsは、v-model(defineModel)で指定することができます🙆‍♂
(記事中で<input>にrefを指定しているのがまさにそれで、ユーザーが定義したコンポーネントにもv-modelは定義できる、という)
(複数指定することもできます)

Ryota AsaiRyota Asai

コメントありがとうございます。
v-model を複数指定する機会があまりなかったので、その点については初めて知りました👀
自身、Vue2を触る機会が多かったので。。。