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

Vue.js(Options API)からReactへの移行でみる違いと比較 #2:メソッド・ステート

最終更新日: Update!!
前回記事「Vue.js(Options API)からReactへの移行でみる違いと比較 #1:コンポーネント」では簡単なTODOアプリを例に、同じ機能をVue.jsとReactで作成した場合で主にコンポーネントについて比較してみました。そこで前回に引き続き、Vue.jsとReactの違いについてみていきます。今回は、コンポーネント内の関数や変数などを中心に比べてみます。また前回同様、Vue.jsは3系のOptions APIで、Reactは関数コンポーネントでの前提でコードを書いています。   まずは前回サンプルとしてあげたアプリケーションのサンプルコードになります。今回はスタイルに関する部分は省略し、テンプレートと処理の部分だけを抜粋しています。Vue.jsとReactではコンポーネント全体の構造や処理の書き方などが異なるのがよくわかります。 【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の方も同様、前回のアプリケーションのサンプルコードになります。コンポーネントの比較については前回記事「Vue.js(Options API)からReactへの移行でみる違いと比較 #1:コンポーネント」をご参考ください。 【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. data(ステート、リアクティブな変数)
Vue.jsではコンポーネント内で扱われる変数を「data」オプションで指定できます。またこの変数は、「v-model」を使ってフォームの値と同期させることができる「双方向バインディング」というのも大きな特徴です。定義した変数は、メソッド内だけでなく、テンプレート上でも扱うことができます。
// Vue.js(Options API)
export default {
  .....
  data() {
    return {
      values: [ 'foo', 'bar', 'baz' ]
    }
  }
  .....
}
  対して、Reactの場合では、「useState」を使うことで、リアクティブな変数として扱うことができます。getter関数とsetter関数の変数にuseStateメソッドを代入し、その引数に初期値を入れることで、値を取得したり更新したりすることができます。ただ、Reactの場合はVue.jsと違い単方向のデータバインディングになるため、フォームの入力値を更新するためには、イベントで値を代入する処理が必要となります。その辺りはまた別記事でまとめてみたいと思います。
// React
import React, { useState } from 'react';
export const App = () => {
  .....
  const [values, setValues] = useState([ 'foo', 'bar', 'baz' ]);
  .....
}
   
2. computed(算出プロパティ)
続いては計算などの処理によって導き出される変数を扱う算出プロパティです。こちらもよく使うものですが、Vue.jsでは「computed」オプションでキーを指定し、その中で、getter関数とsetter関数を定義します。これで先ほどのステート変数の値を返したり更新することができます。
// Vue.js(Options API)
export default {
  .....
  data() {
    return {
      values: [ 'foo', 'bar', 'baz' ]
    }
  },
  computed: {
    someFunction: {
      get: () => this.values,
      set: (argument) => {
        this.values.push(argument);
      }
    }
  }
  .....
}
  Reactの場合には、ステート変数と同じくuseStateを使って値を取得したり、更新したりします。useStateメソッドの代入元では、getter関数とsetter関数が配列で並んでおり、値の取得や更新はこれを使います。値の取得に関しては「useMemo」で算出値をメモ化しておくことで、関係のないところで処理が実行されないようにしておくのもポイントですね。メモ化については過去記事「Reactで値や関数をメモ化してパフォーマンスを改善してみる(React.memo, useMemo, useCallback)」をご参考ください。
// React
import React, { useState, useMemo } from 'react';
export const App = () => {
  .....
  const [values, setValues] = useState([ 'foo', 'bar', 'baz' ]);
  const get = useMemo(() => {
    return values;
  });
  const set = (argument) => {
    setValues([...values, argument]);
  }
  .....
}
   
3. watch(監視プロパティ)
続いては監視プロパティと呼ばれる、何か特定の値が更新されたタイミングで処理を実行できるものになります。Vue.jsでは「watch」オプションで対象の変数と、変数が更新されたときに呼び出される処理を記述していきます。また、Vue.jsの3系では、deepオプションを有効化することで、配列やオブジェクトの値もトリガーの対象とすることができます。
// Vue.js(Options API)
export default {
  .....
  data() {
    return {
      values: [ 'foo', 'bar', 'baz' ]
    }
  },
  watch: {
    values: {
      handler(newValue, oldValue) {
        console.log('値が変わりました');
      },
      deep: true
    }
  },
  .....
}
  Reactでは、「useEffect」フックを使う形になります。このuseEffectメソッドの第二引数で、対象となる値を指定することで、指定した値が更新された時に、コールバック関数として指定した処理が実行されるようになります。
// React
import React, { useState, useEffect } from 'react';
export const App = () => {
  .....
  const [values, setValues] = useState([ 'foo', 'bar', 'baz' ]);
  useEffect(() => {
    console.log('値が変わりました');
  }, [values]);
  .....
}
   
4. methods(コンポーネント内の関数定義)
コンポーネント内で扱われる処理については、Vue.jsの場合ですと「methods」オプションにまとめて記述していきます。ここで定義した関数は、テンプレート内のイベントで呼び出したり、createdなどのライフサイクルフックで実行させたりします。
// Vue.js(Options API)
export default {
  .....
  methods: {
    someFunction() {};
  }
  .....
}
  Reactではそのままコンポーネント内に関数を定義する形となり、とてもシンプルですね。
// React
export const App = () => {
  .....
  const someFunction = () => {};
  .....
}
   
5. created, mounted(ライフサイクルフック)
フロントエンドのフレームワークを扱う上で理解しておく必要があるのが、各種フックになります。Vue.jsではVueのインスタンスの初期化から、DOMにマウントされ、破棄されるまでの一連の流れを「ライフサイクルフック」と呼ばれ、各メソッド内で定義した関数を実行する形となります。今回は「created」と「mounted」のフックをみていきます。
// Vue.js(Options API)
export default {
  .....
  created() {
    console.log('created');
  },
  mounted() {
    console.log('mounted');
  }
  .....
}
  ReactではVue.jsのようなライフサイクルフックに相当するようなものは用意されていないですが、同じくフックは用意されているので、それを理解しておく必要があります。createdの場合には、そのまま関数コンポーネント内で実行する形でOKですが、moutedの場合では「useEffect」を使ってレンダリングされた後に呼び出す必要があります。ただしJSXやTSXではHTMLを返すのでどちらもreturn文の前に実行する点に注意します。
// React
import React, { useEffect } from 'react';
export const App = () => {
  .....
  console.log('created');
  useEffect(() => {
    console.log('mounted');
  }, []);
  .....
  return ()
}
  useEffectは、先述の場合ですと監視プロパティとして使いましたが、第二引数を空配列にすることで、コンポーネントが呼び出された初回のみ実行されるようになり、これでmoutedと同じように使うことができます。ちなみに値を指定しない場合には何回も呼び出されることになるので注意しましょう。  
  今回もVue.jsとReactで作成したアプリで実装方法の違いを比べてみました。それぞれ書き方が異なるので、コンポーネントの時と同様に置き換えられるようにそれぞれの書き方を覚えておきたいですね。
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

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