owned mediaウェブ制作に役立つコンテンツを発信中!

Vue.js(Options API)からReactへの移行でみる違いと比較 #4:イベントハンドリング・バインディング

最終更新日: Update!!
先日より、Vue.jsからReactへの移行を例に、それぞれの違いなどを比較していますが、第4回ではイベントハンドラや属性に変数などを用いるデータバインディングなどをメインに見ていきます。コンポーネントとして扱う場合には処理やデータを持たせることも多いので、とても重要になりますね。また前回までの記事も合わせてご参考ください。 (過去記事) Vue.js(Options API)からReactへの移行でみる違いと比較 #1:コンポーネント Vue.js(Options API)からReactへの移行でみる違いと比較 #2:メソッド・ステート Vue.js(Options API)からReactへの移行でみる違いと比較 #3:テンプレート処理   前回同様、これまでに引き続きサンプルとしてあげているアプリケーションのコードになります。ここではスタイルに関する記述は割愛しています。テンプレートと処理の部分だけを抜粋しています。 【App.vue(Vue.js)】※一部抜粋
<template>
  <h1>TODO List(Vue)</h1>
  <ul class="list">
    <task-item v-for="(task, index) in tasks" :key="`${task}-${index}`" :task-data="{ index, task }" @delete-task="deleteTask" />
  </ul>
  <input v-model="newTask" class="task-input" type="text" placeholder="タスクを入力" />
  <button class="button-add" @click="addTask">追加</button>
  <div v-if="isError" class="error-message">タスクが空欄です</div>
</template>

<script>
  import TaskItem from './components/TaskItem.vue';
  export default {
    components: {
      TaskItem
    },
    data() {
      return {
        tasks: [],
        newTask: null,
        isError: false
      }
    },
    created() {
      this.fetchData();
    },
    methods: {
      fetchData() {
        this.tasks = [ 'タスク1', 'タスク2', 'タスク3' ];
      },
      addTask() {
        if(this.newTask !== null) {
          this.tasks.push(this.newTask);
          this.newTask = null;
          this.isError = false;
        } else {
          this.isError = true;
        }
      },
      deleteTask(payload) {
        this.tasks.splice(payload, 1);
      }
    }
  }
</script>
  【TaskItem.vue(Vue.js)】※一部抜粋
<template>
  <li class="list-item">
    <button class="button-delete" @click="emitDeleteTask(taskData.index)">削除</button>
    <span>{{ taskData.task }}</span>
  </li>
</template>

<script>
  export default {
    name: 'TaskItem',
    props: {
      taskData: {
        type: () => {}
      }
    },
    methods: {
      emitDeleteTask(index) {
        this.$emit('delete-task', index)
      }
    }
  }
</script>
  Reactも同じくサンプルのアプリケーションのソースコードを確認しておきます。テンプレートや処理の書き方などの差異は過去記事でもまとめています。 【App.jsx(React)】
import React, { useState, useEffect, useRef } from 'react';
import { TaskItem } from './components/TaskItem.jsx';
import AppCSS from './App.module.css';

export const App = () => {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState(null);
  const [isError, setError] = useState(false);
  const fetchData = () => {
    setTasks([ 'タスク1', 'タスク2', 'タスク3' ]);
  };
  const dataBinding = (event) => {
    setNewTask(event.target.value)
  };
  const refs = useRef(null);
  const formReset = () => {
    refs.current.value = null
  };
  const addTask = () => {
    if(newTask !== null) {
      setTasks([...tasks, newTask]);
      setNewTask(null);
      setError(false);
    } else {
      setError(true);
    }
  };
  const deleteTask = (payload) => {
    setTasks(
      tasks.filter((task, index) => index !== payload)
    )
  };
  useEffect(() => {
    fetchData();
  }, [])
  return (
    <>
      <h1>TODO List(React)</h1>
      <ul className={AppCSS['list']}>
      {
        tasks.map((task, index) => {
          return <TaskItem 
            taskData={{ index, task }} 
            key={`${task}-${index.toString()}`} 
            methods={
              { deleteTask }
            } 
         />
       })
     }
     </ul>
     <input 
       ref={refs}
       onChange={dataBinding}
       className={AppCSS['task-input']} 
       type="text" 
       placeholder="タスクを入力" 
     />
     <button 
       onClick={() => {
         addTask();
         formReset();
       }}
       className={AppCSS['button-add']}
     >追加</button>
     {
       isError && <div className={AppCSS['error-message']}>タスクが空欄です</div>
     }
     </>
   )
};
  【TaskItem.jsx(React)】
import React from 'react';
import TaskItemCSS from './TaskItem.module.css';

export const TaskItem = (props) => {
  return (
    <>
      <li className={TaskItemCSS['list-item']}>
        <button 
          onClick={() => props.methods.deleteTask(props.taskData.index)} 
          className={TaskItemCSS['button-delete']}
        >削除</button>
        <span>{props.taskData.task}</span>
      </li>
    </>
  )
};
  各種イベントはテンプレート内で各種イベントでのトリガーで実行するような使い方をされることが多いです。そのあたりも踏まえて違いを見ていきます。    
1. v-on、@event(イベントハンドリング)
Vue.jsにおいてテンプレート側でイベントハンドリングを行う際には、「v-on」ディレクティブを使います。clickイベントやchangeイベントなど様々なイベントを扱うことができます。イベントを属性として記述し、値に対象のイベントで実行する関数を指定します。その関数は「methods」のオプションで作成しておきます。また、テンプレート側で「$event」という名前で引数に格納すると処理の中でイベントオブジェクトを受け渡すことができます。なお、v-on:〜は@を使った省略記法も存在します。
// Vue.js(Options API)
<template>
  <button v-on:click="someFunction($event)">Button 1</button>
  <button @click="someFunction($event)">Button 2</button>
</template>

<script>
  export default {
    methods: {
      someFunction(event) {
        const buttonText = event.target.innerText;
        alert(`${buttonText} clicked!`);
      }
    }
  }
</script>
  Reactでは独自の構文が存在しないのですが、HTML内でJavaScript実行するときに書かれるキャメルケースの記法と同じような形で対応することができます。クリックの場合ですと、onClickのイベントを指定しておき、その値のコールバック関数で指定します。こちらも同じくイベントオブジェクトを受け渡すことができます。
// React
import React from 'react';
export const App = () => {
  const someFunction = (event) => {
  const buttonText = event.target.innerText;
    alert(`${buttonText} clicked!`);
  };
  return (
    <>
      <button onClick="{
        (event) => {
          someFunction(event)
        }
      }">Button 1</button>
      <button onClick="{
        (event) => {
          someFunction(event)
        }
      }">Button 2</button>
    </>
  )
};
   
2. v-bind(属性バインディング)
Vue.jsでもReactでも動的に属性の値を切り替えることができます。フロントエンド側のデータによってHTMLなどの見た目の振る舞いを調整する必要も多々ありますが、そんな場合には属性値に変数が格納される形になります。下記はclass属性を動的に切り替える例です。実際にはこのように「v-bind」ディレクティブを使って動的な属性値を入れる形になります。また、属性値では三項演算子を使うことで条件分岐を使ったり、複数の変数をリストの形で指定することもできます。その場合には配列として値を指定します。
// Vue.js(Options API)
<template>
  <p v-bind:class="exampleClassName">class属性に変数受け渡し</p>
  <p :class="[ exampleClassName ? 'bar' : 'baz' ]>class属性に条件分岐</p>
</template>

<script>
  export default {
    data() {
      return {
        exampleClassName: 'foo'
      }
    }
  }
</script>
  Reactでは属性値のバインディングにおいてイベントハンドラと同じく独自の構文はないのですが、属性によっては専用の属性も用意されていることもあります。例としてclass属性の場合には代わりに、「className」属性というものを扱います。そのほかの属性については、同じような記述になりますが動的なものはキャメルケースで扱われることもあるので注意です。
// React
import React, { useState } from 'react';
export const App = () => {
  const [exampleClassName, setExampleClassName] = useState('foo');
  return (
    <>
      <p className={exampleClassName}>class属性に変数受け渡し</p>
      <p className={exampleClassName ? 'bar' : 'baz'}>class属性に条件分岐</p>
    </>
  )
};
   
3. style属性のバインディング
バインドさせる値が、キーを持ったオブジェクトの場合にも同じようにバインドさせた属性値で扱うことができます。その代表例にstyle属性があります。動的にスタイルを切り替えたい時はよく使うのでぜひ覚えておきたいですね。Vue.jsでは下記のように同じく「v-bind」ディレクティブでstyle属性を指定し、値には変数でCSSを指定します。
// Vue.js(Options API)
<template>
  <p :style="elementStyle">style属性に変数受け渡し</p>
</template>

<script>
  export default {
    data() {
      return {
        elementStyle: {
          'color': '#ff0000'
        }
      }
    }
  }
</script>
  Reactでもほとんど同じような形で実装することとなります。処理の中であらかじめスタイルを変数として持たせておき、テンプレート上でそのスタイルが適用されるようにstyle属性の値に代入する形になります、
// React
import React from 'react';
export const App = () => {
  const elementStyle = {
    'color': 'red'
  };
  return (
    <>
      <p style={elementStyle}>style属性に変数受け渡し</p>
    </>
  )
};
   
4. v-model(双方向バインディング)
Vue.jsの大きな特徴として双方向バインディングというのがあります。これはinput要素などのフォームの入力値と変数を同期できるもので「v-model」を使って簡単に実装することができます。対象となる要素にv-modelで変数名を指定する形になります。
// Vue.js(Options API)
<template>
  <input v-model="bindingData" type="text">
  <p>{{ bindingData }}</p>
</template>

<script>
  export default {
    data() {
      return {
        bindingData: 'defaultValue'
      }
    }
  }
</script>
  Reactでは、双方向バインディングの機能は存在しないため、自前で実装してあげる必要があります。フォームの値を変数と同期させる処理を用意して、onChangeのイベントで値が変数に格納されるようにします。Vue.jsと比較して少し実装コストがかかります。
// React
import React, { useState, useEffect, useRef } from 'react';
export const App = () => {
  const [bindingData, setBindingData] = useState('defaultValue');
  const binding = (event) => {
    setBindingData(event.target.value);
  };
  useEffect(() => {
    refs.current.value = bindingData;
  }, []);
  return (
    <>
      <input 
        ref={refs} 
        onChange={(event) => {
          binding(event)
        }}
        type="text"
      />
      <p>{bindingData}</p>
    </>
  )
};
 
  今回はイベントハンドリングと各種属性のバインディングについてVue.jsとReactの違いを見てみました。似たようなものもあれば双方向バインディングなどは機能自体が無いものもあります。用途や目的に合わせて使い分けられるようにしておきたいですね。
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

    • Twitter
    • Github
    contact usscroll to top
      • Facebook
      • Twitter
      • Github
      • Instagram