❓ これは何
MVVMはフロントエンドの開発の設計モデルの一つです。
今回はこれを理解するのに、React TS で簡単なタスク管理アプリを作ってみます。
始めて React TS を始める人もついていける内容かと思います。
15分程度でできるでしょう。
完成すると次のようになります。
🪴 環境
2024年 7月16日(火) 執筆
- apple M1 MacBook Air
- 14.5(23F79)
🛠️ やり方
MVVMは、ソフトウェアアーキテクチャのデザインパターンの一つで、
アプリケーションのロジックとUIを分離することを目的としています。
主に3つの部分から構成されます
- Model(モデル): アプリケーションのデータとビジネスロジックを保持します。
- View(ビュー): ユーザーインターフェースを担当し、ユーザーの入力を受け付けます。
- 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;
以上です!
これでできたはず!!
コメントを残す