MVVMとは?を理解するために簡単なアプリを作る with React

❓ これは何

MVVMはフロントエンドの開発の設計モデルの一つです。
今回はこれを理解するのに、React TS で簡単なタスク管理アプリを作ってみます。

始めて React TS を始める人もついていける内容かと思います。
15分程度でできるでしょう。

完成すると次のようになります。

🪴 環境

2024年 7月16日(火) 執筆

  • apple M1 MacBook Air
  • 14.5(23F79)

🛠️ やり方

MVVMは、ソフトウェアアーキテクチャのデザインパターンの一つで、
アプリケーションのロジックとUIを分離することを目的としています。

主に3つの部分から構成されます

  1. Model(モデル): アプリケーションのデータとビジネスロジックを保持します。
  2. View(ビュー): ユーザーインターフェースを担当し、ユーザーの入力を受け付けます。
  3. ViewModel(ビューモデル): ViewとModelの間のインターフェースとして機能し、データバインディングとUIロジックを処理します。

これを聞いても意味がわからないと思います。
とりあえず簡単なタスク管理アプリを作りながら、考えていきましょう。

🏃‍♀️ プロジェクトの作成

プロジェクトを作成します。

yarn create react-app task-tracker --template typescript

プロジェクトフォルダに移動します。

cd task-tracker

起動します。

yarn start

ブラウザで一応確認

📦 Model

モデルは、アプリケーションのデータ構造を定義します。
モデルはデータの保存、取得、操作を行います。

今回は使っていませんが、
データの永続化(データベースへの保存など)や
外部からのデータ取得(APIからのデータ取得など)を担当します。

src ディレクトリに models フォルダを作成し、
中にTask.ts を作成します。

export interface Task {
  id: number;
  title: string;
  completed: boolean;
}

🚀 ViewModel

ビューモデルは、モデルとビューの間の橋渡しを行います。
タスクの追加、完了状態の切り替え、削除のロジックを管理します。

src/models ディレクトリに TaskViewModel.tsx を作成

このコードは、タスクの状態を管理するためのフックを定義しています。
useTaskViewModelフックを使用することで、タスクの追加、完了状態の切り替え、削除が簡単に行えます。

import { useState } from 'react';
import { Task } from './Task';

export const useTaskViewModel = () => {
  const [tasks, setTasks] = useState<Task[]>([]);

  const addTask = (title: string) => {
    const newTask: Task = {
      id: Date.now(),
      title,
      completed: false,
    };
    setTasks([...tasks, newTask]);
  };

  const toggleTaskCompletion = (id: number) => {
    setTasks(tasks.map(task => 
      task.id === id ? { ...task, completed: !task.completed } : task
    ));
  };

  const deleteTask = (id: number) => {
    setTasks(tasks.filter(task => task.id !== id));
  };

  return {
    tasks,
    addTask,
    toggleTaskCompletion,
    deleteTask,
  };
};

🐏 Model, View があれば ViewModel いらなくない?

モデルとビューだけではコードが煩雑になります。

  • ビューが直接モデルを操作すると、ビューにロジックが含まれることになります。
    これにより、ビューが単純に見た目のみの表現でなくなってしまうので、再利用性が低くなります。
  • ViewModelがあると単純にテストがしやすくなります。パラメータの調整も楽です。

👀 View

ビューは、ユーザーインターフェースを担当し、ユーザーの操作を受け付けます。
ここでは、タスクのリスト表示とタスクの追加を行うコンポーネントを作成します。

src/components ディレクトリに TaskList.tsx を作成

タスクをリスト表示し、完了状態の切り替えや削除を行うコンポーネントです。

// src/components/TaskList.tsx
import React from 'react';
import { Task } from '../models/Task';

interface TaskListProps {
  tasks: Task[];
  onToggle: (id: number) => void;
  onDelete: (id: number) => void;
}

const TaskList: React.FC<TaskListProps> = ({ tasks, onToggle, onDelete }) => {
  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          <input 
            type="checkbox" 
            checked={task.completed} 
            onChange={() => onToggle(task.id)} 
          />
          {task.title}
          <button onClick={() => onDelete(task.id)}>Delete</button>
        </li>
      ))}
    </ul>
  );
};

export default TaskList;

src/components ディレクトリに AddTask.tsx を作成

// src/components/AddTask.tsx
import React, { useState } from 'react';

interface AddTaskProps {
  onAdd: (title: string) => void;
}

const AddTask: React.FC<AddTaskProps> = ({ onAdd }) => {
  const [title, setTitle] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (title.trim()) {
      onAdd(title);
      setTitle('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={title} 
        onChange={(e) => setTitle(e.target.value)} 
        placeholder="Add a new task" 
      />
      <button type="submit">Add</button>
    </form>
  );
};

export default AddTask;

🍳 仕上げ

最後に、AppコンポーネントでこれらのコンポーネントとViewModelを統合します。

プロジェクトの基本となる App.tsx を次のように書き換えます。

この構造により、アプリケーションのロジック(タスクの追加、完了状態の切り替え、削除)はViewModelに集中します。

ビューはデータの表示とユーザーの入力を受け取ることに専念できます。
これにより、コードの再利用性とテスト容易性が向上します。

// src/App.tsx
import React from 'react';
import './App.css';
import { useTaskViewModel } from './models/TaskViewModel';
import TaskList from './components/TaskList';
import AddTask from './components/AddTask';

const App: React.FC = () => {
  const { tasks, addTask, toggleTaskCompletion, deleteTask } = useTaskViewModel();

  return (
    <div className="App">
      <h1>Task Tracker</h1>
      <AddTask onAdd={addTask} />
      <TaskList 
        tasks={tasks} 
        onToggle={toggleTaskCompletion} 
        onDelete={deleteTask} 
      />
    </div>
  );
};

export default App;

以上です!
これでできたはず!!

coiai.netでは案件受付中です。
Vtuber、建築モデリング、EC構築、ネイティブアプリ制作、制服の制作(実は縫製業もメイン)、Vision Pro向けアプリ etc…
様々な制作開発を行っています。

ご気軽にご相談ください!

コイアイちゃんのアバター

この記事を書いたのは


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


Top
Blog
Works
contact