はじめに
この記事はPLEX Advent Calendar 2024の 13 日目の記事です。
Vue.js v2 のサポートが 2023 年 12 月 31 日に終了した事もあり、Vue.js/Nuxt v2のプロダクトをReact/Next.jsへリプレイスする作業を行いました。
その際に Vue.js v2 と React の記法の違いについて少し戸惑うところがあったため、本記事では、Vue.js v2 の基本的な API(data
、methods
、computed
など) を React ではどのように置き換えるかについて、コード例を交えて解説します。
主要な API の置き換え方法
Vue.js v2 の主要 API を React でどのように置き換えるかについて、具体的な例を見ていきましょう。
1.data
Vue.js v2 のdata
はリアクティブな状態(オブジェクトのデータ)を返す関数です。
ここで定義されたデータはリアクティブなものとして扱われます。
Vue.js v2
<template> <div> <p>{{ message }}</p> </div> </template> <script> export default { data() { return { message: "Hello Vue!", }; }, }; </script>
React
React ではuseState
を用いて置き換えます。
import React, { useState } from "react"; function App() { const [message, setMessage] = useState("Hello React!"); return ( <div> <p>{message}</p> </div> ); } export default App;
2.methods
Vue.js v2 のmethods
はコンポーネント内で使用されるメソッドを定義するオブジェクトです。
ユーザーのイベント(クリックなど)に対応する際や、データ操作のロジックを持つ関数などを定義します。
Vue.js v2
<template> <div> <button @click="sayHello">Click me</button> </div> </template> <script> export default { methods: { sayHello() { alert("Hello Vue!"); }, }, }; </script>
React
React ではコンポーネント内で関数を定義します。
import React from "react"; function App() { const sayHello = () => { alert("Hello React!"); }; return ( <div> <button onClick={sayHello}>Click me</button> </div> ); } export default App;
3.computed
Vue.js v2 のcomputed
は依存しているデータに基づいて計算されるプロパティを定義するオブジェクトです。
ここに定義したデータはキャッシュされて、依存するデータが変更された場合にのみ再計算されます。
Vue.js v2
<template> <div> <p>{{ reversedMessage }}</p> </div> </template> <script> export default { data() { return { message: "Hello Vue!", }; }, computed: { reversedMessage() { return this.message.split("").reverse().join(""); }, }, }; </script>
React
React ではuseMemo
で返す値をメモ化します。
import React, { useState, useMemo } from "react"; function App() { const [message, setMessage] = useState("Hello React!"); // messageが更新された時のみ再計算 const reversedMessage = useMemo(() => { return message.split("").reverse().join(""); }, [message]); return ( <div> <p>{reversedMessage}</p> </div> ); } export default App;
4.props
Vue.js v2 のprops
は親コンポーネントから子コンポーネントにデータを渡すためのプロパティを定義するオブジェクトです。
子コンポーネントはprops
で宣言されたプロパティを通じて、親から渡されたデータにアクセス可能です。
Vue.js v2
- 子コンポーネント(Message.vue)
<template> <div> <p>{{ message }}</p> </div> </template> <script> export default { name: "Message", // 親からのデータ(:message="Hello Vue!")の箇所を受け取る props: { message: { type: String, required: true, }, }, }; </script>
<template> <div> <Message :message="Hello Vue!" /> <Message :message="Hello Vue v2!" /> </div> </template> <script> import Message from "./Message.vue"; export default { name: "App", components: { Message } }; </script>
React
React ではコンポーネントの引数にprops
を渡します。
import React from "react"; // 子コンポーネント function Message({ message }) { return <p>{message}</p>; } // 親コンポーネント function App() { return ( <div> <Message message="Hello React!" /> </div> ); } export default App;
5.emit
Vue.js v2
Vue.js v2 のemit
は子コンポーネントが親コンポーネントにイベントを発火させたりデータを渡すための機能です。
- 子コンポーネント(Button.vue)
<template> <div> <button @click="sendMessage">Click me</button> </div> </template> <script> export default { methods: { sendMessage() { // イベント名と親コンポーネントに送信する値 this.$emit("onClickMessage", "Hello from child!"); }, }, }; </script>
<template> <div> <!-- @onClickは子のemitのイベント名 --> <Button @onClickMessage="getMessage" /> {{ message }} </div> </template> <script> import Button from "./Button.vue"; export default { name: "App", components: { Button, }, data() { return { message: "", }; }, methods: { // value = 子コンポーネントから受け取る値(Hello from child!) getMessage(value) { this.message = value; }, }, }; </script>
React
React では親コンポーネントから子コンポーネントにprops
としてイベントハンドラを渡すことで親にイベントを発火できます。
import React from "react"; function Button({ onClickMessage }) { const sendMessage = () => { onClickMessage("Hello from child!"); }; return <button onClick={sendMessage}>Click me</button>; } function App() { const handleMessage = (message) => { console.log(message); // "Hello from child!" }; return ( <div> <Button onClickMessage={handleMessage} /> </div> ); } export default App;
6.watch
Vue.js v2 のwatch
は特定のデータの変更を監視し、変更時に特定の関数を実行するオブジェクトです。
データが変更されたときに別の処理を実行したい場合に役立ちます。
Vue.js v2
<template> <div> <!-- 入力値とVueインスタンスのデータを同期 --> <input v-model="message" /> </div> </template> <script> export default { data() { return { message: "", }; }, watch: { // 監視したいデータ名(message) message(newVal, oldVal) { console.log(`Message changed from ${oldVal} to ${newVal}`); }, }, }; </script>
React
React ではuseEffect
の引数の依存配列箇所に監視する state(ここでは message)を指定します。
import React, { useState, useEffect } from "react"; function App() { const [message, setMessage] = useState(""); useEffect(() => { console.log(`Message changed to ${message}`); }, [message]); return ( <div> <input value={message} onChange={(e) => setMessage(e.target.value)} /> </div> ); } export default App;
7.ref
Vue.js v2 のref
はコンポーネント内の DOM 要素や子コンポーネントに直接アクセスするための機能です。
Vue.js v2
<template> <div> <!-- refの定義 --> <input ref="myInput" /> <button @click="focusInput">Focus Input</button> </div> </template> <script> export default { methods: { focusInput() { // inputのDOMにアクセス this.$refs.myInput.focus(); }, }, }; </script>
React
React ではuseRef
を用います。
import React, { useRef } from "react"; function App() { const inputRef = useRef(null); const focusInput = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} /> <button onClick={focusInput}>Focus Input</button> </div> ); } export default App;
ライフサイクルフックの置き換え方法
Vue.js v2 のライフサイクルフックは、Vue インスタンスの生成から破棄までの一連の間に実行される関数のことです。
React では主にuseEffect
を使用することで同等の機能が実現可能です。
1.mounted
インスタンスが DOM 要素にマウント(描画)された後に呼ばれる処理です。
Vue.js v2
<script> export default { mounted() { console.log("mounted"); }, }; </script>
React
React ではuseEffect
を使用して依存配列を空にすることで、マウント後に一度だけ実行されるようになります。
import React, { useState, useEffect } from "react"; function App() { useEffect(() => { console.log("mounted"); }, []); return ( // 略 ); } export default App;
2.updated
データが変更・更新され DOM が再レンダリングされた後に毎回呼ばれる処理です。
Vue.js v2
<script> export default { updated() { console.log("updated"); }, }; </script>
React
React ではuseEffect
を使用して第二引数の依存配列を無しにすることでstate
が変更された後に毎回実行されます。
import React, { useState, useEffect } from "react"; function App() { const [message, setMessage] = useState("Hello React!"); useEffect(() => { console.log("updated"); }); return ( <div> <p>{message}</p> <button onClick={() => setMessage("Updated message!")}> Update Message </button> </div> ); } export default App;
3.destroyed
インスタンスが破棄された後に呼び出されるフックです。
Vue.js v2
<script> export default { destroyed() { console.log("destroyed"); }, }; </script>
React
React ではuseEffect
のクリーンアップ関数を使用します。
import React, { useState, useEffect } from "react"; function App() { const [message, setMessage] = useState("Hello React!"); useEffect(() => { // クリーンアップ return () => { console.log("destroyed"); }; }, []); return ( <div> <p>{message}</p> </div> ); } export default App;
ディレクティブの置き換え方法
Vue.js v2 のディレクティブはv-
から始まる独自の HTML 属性です。
値には JavaScript の式や data、メソッドなどを定義して、属性値の変化による副作用をリアクティブに DOM に適用します。
1. v-if, v-else-if, v-else
Vue.js v2 のv-if
, v-else-if
, v-else
は条件付きで要素のレンダリングを制御します。
Vue.js v2
<template> <div> <p v-if="isLoggedIn">ログイン済みです。</p> <p v-else-if="isGuest">ゲストとして閲覧中です。</p> <p v-else>ログインしてください。</p> </div> </template> <script> export default { data() { return { isLoggedIn: false, isGuest: true, }; }, }; </script>
React
React では三項演算子を JSX 内で使用します。
import React from "react"; function App() { const isLoggedIn = false; const isGuest = true; return ( <div> {isLoggedIn ? ( <p>ログイン済みです。</p> ) : isGuest ? ( <p>ゲストとして閲覧中です。</p> ) : ( <p>ログインしてください。</p> )} </div> ); } export default App;
2. v-show
v-show
は CSS のdisplay
プロパティで要素の表示/非表示を制御します。
Vue.js v2
<template> <div> <p v-show="isVisible">この要素は表示されています。</p> </div> </template> <script> export default { data() { return { isVisible: true, }; }, }; </script>
React
React では、条件によってstyle
属性を変更します。
import React from "react"; function App() { const isVisible = true; return ( <div> <p style={{ display: isVisible ? "block" : "none" }}> この要素は表示されています。 </p> </div> ); } export default App;
3. v-for
v-for
は配列やオブジェクトをループして、要素を繰り返し描画します。
Vue.js v2
<template> <ul> <li v-for="(item, index) in items" :key="index">{{ item }}</li> </ul> </template> <script> export default { data() { return { items: ["リンゴ", "バナナ", "オレンジ"], }; }, }; </script>
React
React では、map
メソッドを使用します。
import React from "react"; function App() { const items = ["リンゴ", "バナナ", "オレンジ"]; return ( <ul> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); } export default App;
4. v-model
v-model
はフォーム要素とデータの双方向バインディングを実現します。
下記では入力に連動して data
の message に入力内容が追加されます。
Vue.js v2
<template> <input v-model="message" /> </template> <script> export default { data() { return { message: "", }; }, }; </script>
React
React ではuseState
を使い、value
属性とonChange
イベントを連携させます。
import React, { useState } from "react"; function App() { const [message, setMessage] = useState(""); return <input value={message} onChange={(e) => setMessage(e.target.value)} />; } export default App;
5. v-bind
v-bind
は様々な HTML の属性やコンポーネントのプロパティにデータを動的にバインド出来ます。
Vue.js v2
<template> <img v-bind:src="imageSrc" /> <img :src="imageSrc" /><!-- 「:属性名」で省略記法となる --> </template> <script> export default { data() { return { imageSrc: "https://picsum.photos/id/237/200/300", }; }, }; </script>
React
React では、プロパティをそのまま JSX で指定します。
import React from "react"; function App() { const imageSrc = "https://picsum.photos/id/237/200/300"; return <img src={imageSrc} />; } export default App;
6. v-on
v-on
はイベントリスナーを登録する際に使用されます。
Vue.js v2
<template> <button v-on:click="handleClick">クリック</button> <button @click="handleClick">クリック</button ><!-- 「@イベント名」で省略記法となる --> </template> <script> export default { methods: { handleClick() { alert("クリックされました"); }, }, }; </script>
React
React では、イベントハンドラーを直接指定します。
import React from "react"; function App() { const handleClick = () => { alert("クリックされました"); }; return <button onClick={handleClick}>クリック</button>; } export default App;
おわりに
今回はVue.js v2 の基礎機能をReactで書き直す方法についてご紹介しました。
実際にはリプレイスする規模感によって両者の記法を参考にしただけでは事足りなくなるかと思いますが、 同じような状況に立つ方の第一歩として参考にしていただければ幸いです。