VueとReactのpropsの違い
最近は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はコンポーネントのインターフェースそのものであり、保守性と再利用性を向上させてくれる。
参考文献
Discussion
まさしく蛇足ですが、Vueで「このpropsは子コンポーネント側で変更して欲しい(変更を許可する)」という特別なpropsは、v-model(defineModel)で指定することができます🙆♂
(記事中で<input>にrefを指定しているのがまさにそれで、ユーザーが定義したコンポーネントにもv-modelは定義できる、という)
(複数指定することもできます)
コメントありがとうございます。
v-model を複数指定する機会があまりなかったので、その点については初めて知りました👀
自身、Vue2を触る機会が多かったので。。。