
記事の概要
この記事では、Facebookが開発したオープンソースのプログラミング言語 React 18.3 を使って、macOS Sonoma と Docker Desktop 4.32 を使った開発環境の構築からシンプルなWebアプリ開発、実行までの流れを紹介します。
この記事を参考に、React や Webアプリ開発 のスタートの一助となれば幸いです。
今回の記事の対象者
この記事は次のような方に向けています:
- React を使ってWebアプリ開発を始めたい方
- macOSとDockerコンテナを使って React の開発環境を構築したい方
- シンプルなWebアプリでReactの実装イメージを理解したい方
はじめに
今回は、構築する環境と、開発するWebアプリの概要は以下の通りです。
開発・実行環境の構成
- サーバー環境
- OS:macOS Sonoma
- ツール:Docker Desktop
- 環境:React コンテナ開発・実行環境
開発・実行環境の効率化ポイント
- Dockerコンテナで環境を統一
- Dockerコンテナを使えば、どのPCやMacでも同じ開発環境を簡単に作れます。友達や同僚と一緒に同じプロジェクトを開発するときに、とても便利です。
- Reactの環境を他の開発者に同一の環境を展開出来る様に設定ファイルで構成をコード化します。
- Dockerコンテナを使ったシンプルな開発環境
- Dockerを使うことで、MacBook等の端末に直接Reactの環境をインストールせずに、コンテナ内で動かすことができます。これにより、環境設定のトラブルが少なく、スムーズに開発が進められます。
Webアプリ
シンプルな簡易ToDoリストアプリを通じて、Reactの基本操作を学びながらReactの重要な要素を実践的に理解できます。
簡易ToDoリストアプリ
概要:
ユーザーがやるべきタスクを入力し、そのタスクをリストに追加できるシンプルなToDoリストアプリです。タスクの追加、完了、削除ができ、フォーム処理、状態管理やリストのレンダリングを学ぶことができます。
学習ポイント:
これらのポイントを学ぶことで、Reactを使って効率的にアプリケーションを開発できるようになります。
- フォーム処理
- ユーザー入力を取得し、タスクを追加するための入力フォーム、データ送信の方法。
- 状態管理
- タスクのリストや入力内容を管理するための、フック(関数)であるuseStateの利用方法。
- リストのレンダリング
- 複数のタスクを画面に表示するために、リストの中身を取り出して表示する方法。
- コンポーネントの再利用
- アプリをパーツごとに分けて管理し、再利用可能にする方法。
もくじ
ソフトウェア・ハードウェア
必要なツール、ライブラリ、端末は以下の通りです。
開発ツール
以下、開発ツールとその公式サイトの一覧です。本記事ではVisual Studio Code(以下 VS Code)を使いますが、Jetbrains社のWebStormは非常に高機能なので、導入しておくと開発作業が楽になります。
端末
以下、今回の環境を構築する対象の端末スペックです。
項目 | 詳細 |
---|---|
ハードウェア | Apple Silicon M3, RAM 24GB |
OS | macOS Sonoma 14.6 |
本記事で紹介するソフトウェアおよびツールは、筆者の個人的な使用経験に基づくものであり、公式のサポート外の設定や使用方法を含む場合があります。利用に際しては、公式サイトの指示およびガイドラインを参照し、自己責任で行ってください。
React概要
Reactとは
WebサイトやWebアプリのユーザーインターフェース(見た目や操作部分)を効率的に作るためのJavaScriptライブラリです。ライブラリとは、よく使われる機能があらかじめまとめられたツールのセットのようなものです。
Reactは、簡単に使い始められる一方で、大規模なアプリケーションにも適していて、FacebookやInstagramといった有名なWebサービスもReactを使って作られています。
Reactの主な特徴
- コンポーネントベースのアプローチ
- Reactの大きな特徴は「コンポーネント」と呼ばれる小さなパーツを作って、それを組み合わせてWebページを構築できることです。たとえば、ボタンやメニュー、フォームといったものを一つひとつコンポーネントとして作り、それらを組み合わせてページ全体を作成します。
- 効率的な更新
- Reactは「仮想DOM」という仕組みを使って、Webページの変更をとても効率よく行います。Webページの状態が変わったときに、実際の画面に反映する部分だけを更新するため、動作が速くてスムーズです。
- 再利用可能なコード
- Reactで作ったコンポーネントは、他の場所でも簡単に再利用できます。たとえば、一度作ったボタンのデザインや動きを他のWebページでもそのまま使うことができます。これにより、同じコードを繰り返し書く手間が省けます。
Dockerコンテナによるサーバー環境の構築
ReactでのToDoリストアプリ開発と実行を、macOSとDocker Composeを使って行う手順を説明します。
- Docker Desktopのインストール
- プロジェクトディレクトリの作成
- Dockerfileの作成
- docker-compose.ymlの作成
- package.json の作成
Docker Desktopのインストール
Reactの環境は、Node.jsの環境をDockerコンテナで構築するので、Docker Desktopを事前にインストールしておきます。
Docker Desktopのインストールについては、以下の記事で詳細に解説していますので、こちらをご覧下さい。
プロジェクトディレクトリの作成
本ステップでのプロジェクトディレクトリの構成を以下に示します。
(base) xxxxxx@xxxxxx react-todo-app % tree .
.
├── Dockerfile
├── docker-compose.yml
└── package.json
まずは、ターミナルで、プロジェクトのためのディレクトリを作成します。
mkdir react-todo-app
cd react-todo-app
Dockerfile の作成
Reactアプリケーションを動かすためのNode.jsコンテナの設定ファイルです。プロジェクトディレクトリに Dockerfile を作成し、以下の内容を記述します。
# Node.jsの公式イメージをベースにする
FROM node:18
# アプリケーションディレクトリを作成
WORKDIR /usr/src/app
# package.json と package-lock.json をコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm install
# アプリケーションソースコードをコピー
COPY . .
# 開発サーバーを起動
CMD ["npm", "start"]
docker-compose.yml の作成
Docker Compose でReactアプリケーションを管理するための設定ファイルを作成します。
services:
react-app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
stdin_open: true
tty: true
定義内容の解説
- services
- 実際に動かすコンテナの定義を行う場所です。ここでは、react-app というサービス(コンテナ)が定義されています。
- サービスは、アプリケーションのコンテナ(環境)を指します。Reactアプリを動かすためのコンテナを「サービス」として設定します。
- react-app
- react-app は、コンテナの名前です。この名前は自由に付けられますが、アプリの内容に即したものにすると分かりやすいです。今回は「Reactアプリケーション」を動かすコンテナなので、react-app という名前にしています。
- build: .
- コンテナのビルド方法を指定します。今回は、カレントディレクトリ(現在のプロジェクトディレクトリ、つまり「.」)にある Dockerfile を使ってコンテナをビルドします。
- Dockerfile には、Reactアプリケーションを動かすために必要な設定(Node.jsのインストールなど)が書かれています。このファイルを基にコンテナが作られます。
- ports:
- ホスト(自分のパソコン)とコンテナの間で、どのポート番号を使用するかを指定します。
- “3000:3000” という設定は、ホストのポート3000をコンテナのポート3000に対応させることを意味しています。これにより、ブラウザで http://localhost:3000 にアクセスすると、コンテナ内で動いているReactアプリが表示されるようになります。
- volumes:
- volumes は、ホストのファイルシステムとコンテナ内のファイルシステムを共有するための設定です。
- ”.:/usr/src/app” は、ホスト(自分のパソコン)上の現在のディレクトリ(「.」)を、コンテナ内の /usr/src/app というディレクトリにマウント(共有)しています。これにより、ホストでファイルを変更すると、コンテナ内でも自動的にその変更が反映されます。
- ”/usr/src/app/node_modules” は、コンテナ内の node_modules ディレクトリをコンテナの外にマウントする設定です。これにより、ホスト側には依存関係の管理ファイルが保存されず、コンテナ内部で管理されることになります。
- stdin_open: true
- このオプションは、コンテナがインタラクティブなモードで動作するようにします。つまり、コンテナ内での対話型の作業(ターミナルでの操作)ができるようになります。
- tty: true
- tty は、コンテナが仮想のターミナル(TTY)を使用するかどうかを指定します。stdin_openとセットで使われることが多く、対話型の操作を行う際に有効になります。
package.json の作成
Reactの依存関係を記述するためのファイルを作成します。
このファイルは、Reactアプリケーションの基本的な情報や、必要なパッケージのバージョンを管理します。このファイルで、他の開発者が同じ環境でプロジェクトを動かすことができます。
主な構成要素を以下に示します。
- プロジェクトの基本情報: 名前やバージョン、説明。
- スクリプトの自動化: よく使うコマンドを登録し、簡単に実行できるようにしています。
- 依存関係の管理: アプリを動かすために必要なライブラリ(Reactやその関連ツール)を管理します。
package.json
{
"name": "react-todo-app",
"version": "1.0.0",
"description": "A simple ToDo list app",
"main": "index.js",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "latest"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
定義内容の解説
name
"name": "react-todo-app"
- このプロジェクトの名前です。
react-todo-app
という名前を指定しています。パッケージとして他の人に配布する場合や、チーム開発を行う際に役立ちます。
version
"version": "1.0.0"
- このプロジェクトのバージョン番号です。最初のリリースなので
1.0.0
という値が設定されています。後でアプリをアップデートした際に、この番号を変更することでバージョン管理を行います。
description
"description": "A simple ToDo list app"
- このプロジェクトの説明です。
A simple ToDo list app
は、このアプリがシンプルなToDoリストアプリであることを英語で示しています。
main
"main": "index.js"
- プロジェクトのエントリーポイントを指定します。エントリーポイントとは、Reactアプリが最初に実行を開始するファイルのことです。
scripts
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
scripts
は、よく使うコマンドを簡単に実行できるように登録しておく場所です。たとえば、開発中にアプリを起動したり、テストを実行したりするときに、このscripts
で定義されたコマンドを使います。- 今回定義したコマンド:
"start"
:npm start
というコマンドでReactの開発サーバーを起動します。ブラウザでリアルタイムにアプリを確認しながら開発できます。"build"
:npm run build
で、アプリを本番環境向けに最適化してビルドします。これにより、効率的に実行できるようにファイルが圧縮されます。"test"
:npm test
で、テストを実行します。開発中にアプリが正しく動いているか確認するための機能です。"eject"
:npm run eject
で、Reactの標準設定から独自設定に切り替えます。通常はReactの設定をそのまま使いますが、カスタマイズしたい場合に使うコマンドです(初心者には推奨されません)。
dependencies
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "latest"
}
dependencies
は、プロジェクトが依存しているライブラリやパッケージを記述する場所です。ここでは、Reactアプリを動かすために必要なライブラリを指定します。- 今回定義した依存関係:
"react": "^18.0.0"
: React自体のライブラリです。^18.0.0
という指定は、バージョン18系のライブラリを使うという意味です(18.1や18.2のバージョンでも動作可能)。"react-dom": "^18.0.0"
: Reactで作ったコンポーネントをブラウザに表示するためのライブラリです。"react-scripts": "latest"
: Reactの開発を簡単にするためのツールセットです。これには開発サーバー、ビルドツール、テストツールなどが含まれており、初心者でも簡単にReact開発を始められます。
簡易ToDoリストアプリの開発
本ステップでの、プロジェクトディレクトリの構成を以下に示します。
(base) xxxxxx@xxxxxx react-todo-app % tree .
.
├── Dockerfile
├── docker-compose.yml
├── package.json
├── public
│ └── index.html
└── src
├── App.js
├── TaskForm.js
├── TaskItem.js
├── TaskList.js
├── index.css
└── index.js
publicディレクトリの作成とindex.htmlの作成
プロジェクトのルートディレクトリに public というフォルダを作成します。 public フォルダ内に index.html ファイルを作成し、以下のような基本的なHTMLコードを追加します。
index.html
index.html はReactアプリケーションがブラウザで表示される際に必要なHTMLファイルです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>React ToDo App</title>
<link href="<https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap>" rel="stylesheet">
</head>
<body>
<div id="root"></div>
</body>
</html>
src ディレクトリの作成
アプリケーションのソースコードを src フォルダに作成します。プロジェクトディレクトリにsrcディレクトリを作成してください。
次に必要なソースコードを作成していきます。
src/index.css
基本的なスタイルを追加します。今回は以下の内容で、シンプルなスタイルを指定します。
/* 全体のフォント設定 */
body {
font-family: 'Roboto', sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* レスポンシブなコンテナ */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
max-width: 600px;
width: 100%;
margin: 0 auto;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
}
/* ヘッダーとフッター */
header, footer {
background-color: #007bff;
color: white;
text-align: center;
padding: 10px 0;
}
footer {
font-size: 0.9em;
}
/* タスクのカードスタイル */
.task-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 15px;
margin: 10px 0;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 完了タスクのスタイル */
.task-card.completed {
background-color: #f0f0f0;
text-decoration: line-through;
color: #888;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* ボタンスタイル */
button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
button:active {
transform: scale(0.98);
}
/* ダークモード */
body.dark-mode {
background-color: #121212;
color: white;
}
.task-card.dark-mode {
background-color: #333;
color: white;
}
src/index.js
Reactアプリのエントリーポイントです。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
src/App.js
アプリケーション全体を管理するコンポーネントです。
import React, { useState } from 'react';
import TaskForm from './TaskForm';
import TaskList from './TaskList';
import './index.css';
function App() {
const [tasks, setTasks] = useState([]);
const [isDarkMode, setIsDarkMode] = useState(false);
const addTask = (taskText) => {
setTasks([...tasks, { text: taskText, completed: false }]);
};
const completeTask = (index) => {
const newTasks = tasks.map((task, i) =>
i === index ? { ...task, completed: !task.completed } : task
);
setTasks(newTasks);
};
const deleteTask = (index) => {
const newTasks = tasks.filter((_, i) => i !== index);
setTasks(newTasks);
};
const toggleDarkMode = () => {
setIsDarkMode(!isDarkMode);
document.body.classList.toggle('dark-mode', !isDarkMode);
};
return (
<div className={`App ${isDarkMode ? 'dark-mode' : ''}`}>
<header>
<h1>ToDoリストアプリ</h1>
</header>
<div className="container">
<button onClick={toggleDarkMode}>ダークモード切り替え</button>
<TaskForm onAddTask={addTask} />
<TaskList tasks={tasks} onCompleteTask={completeTask} onDeleteTask={deleteTask} />
</div>
<footer>
<p>© 2024 Your Name</p>
</footer>
</div>
);
}
export default App;
src/TaskForm.js
タスクを追加するフォームのコンポーネントです。
import React, { useState } from 'react';
function TaskForm({ onAddTask }) {
const [taskText, setTaskText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (taskText.trim() === '') {
alert('タスクを入力してください!');
return;
}
onAddTask(taskText);
setTaskText('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={taskText}
onChange={(e) => setTaskText(e.target.value)}
placeholder="タスクを入力"
/>
<button type="submit">追加</button>
</form>
);
}
export default TaskForm;
src/TaskList.js
タスクリストを表示するコンポーネントです。
import React from 'react';
import TaskItem from './TaskItem';
function TaskList({ tasks, onCompleteTask, onDeleteTask }) {
return (
<div>
{tasks.map((task, index) => (
<TaskItem
key={index}
task={task}
onCompleteTask={() => onCompleteTask(index)}
onDeleteTask={() => onDeleteTask(index)}
/>
))}
</div>
);
}
export default TaskList;
src/TaskItem.js
個別のタスクを表示するコンポーネントです。
import React from 'react';
function TaskItem({ task, onCompleteTask, onDeleteTask }) {
return (
<div className={`task-card ${task.completed ? 'completed' : ''}`}>
{task.text}
<div>
<button onClick={onCompleteTask}>
{task.completed ? '未完了にする' : '完了'}
</button>
<button onClick={onDeleteTask}>削除</button>
</div>
</div>
);
}
export default TaskItem;
Dockerイメージのビルド
プロジェクトディレクトリで以下のコマンドを実行し、Dockerイメージをビルドします。
docker compose build
Dockerイメージをビルドしたログを以下に示します。
(base) xxxxxx@xxxxxx src % docker compose build
[+] Building 52.0s (10/10) FINISHED docker:desktop-linux
=> [react-app internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 425B 0.0s
=> [react-app internal] load metadata for docker.io/library/node:18 2.6s
=> [react-app internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [react-app internal] load build context 0.0s
=> => transferring context: 3.92kB 0.0s
=> [react-app 1/5] FROM docker.io/library/node:18@sha256:ca07c02d13baf021ff5aadb3b48bcd1fcdd4 9.8s
=> => resolve docker.io/library/node:18@sha256:ca07c02d13baf021ff5aadb3b48bcd1fcdd454826266ac 0.0s
=> => sha256:ca07c02d13baf021ff5aadb3b48bcd1fcdd454826266ac313ce858676e8c1548 6.41kB / 6.41kB 0.0s
中略
=> => extracting sha256:385b8718ea4a4ee23758571b52ca3b62730fa006e6865c0606988a88e494138a 0.0s
=> [react-app 2/5] WORKDIR /usr/src/app 0.4s
=> [react-app 3/5] COPY package*.json ./ 0.0s
=> [react-app 4/5] RUN npm install 37.5s
=> [react-app 5/5] COPY . . 0.0s
=> [react-app] exporting to image 1.6s
=> => exporting layers 1.6s
=> => writing image sha256:fb8e26a0f3e312bdbf361b9f6fe5394f21319bcf92b1d9843c8ec6c5a3a54cf7 0.0s
=> => naming to docker.io/library/react-todo-app-react-app 0.0s
(base) xxxxxx@xxxxxx src %
Reactアプリの起動
Docker ComposeでReactアプリを起動します。
docker compose up
Reactアプリを実行したログを以下に示します。
Compiled successfully!
react-app-1 |
react-app-1 | You can now view react-todo-app in the browser.
react-app-1 |
react-app-1 | Local: <http://localhost:3000>
react-app-1 | On Your Network: <http://172.18.0.2:3000>
react-app-1 |
react-app-1 | Note that the development build is not optimized.
react-app-1 | To create a production build, use npm run build.
react-app-1 |
react-app-1 | webpack compiled successfully
react-app-1 | One of your dependencies, babel-preset-react-app, is importing the
react-app-1 | "@babel/plugin-proposal-private-property-in-object" package without
react-app-1 | declaring it in its dependencies. This is currently working because
react-app-1 | "@babel/plugin-proposal-private-property-in-object" is already in your
react-app-1 | node_modules folder for unrelated reasons, but it may break at any time.
react-app-1 |
react-app-1 | babel-preset-react-app is part of the create-react-app project, which
react-app-1 | is not maintianed anymore. It is thus unlikely that this bug will
react-app-1 | ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
react-app-1 | your devDependencies to work around this error. This will make this message
react-app-1 | go away.
react-app-1 |
これで、Reactの開発サーバーが起動し、ブラウザで http://localhost:3000 にアクセスするとToDoリストアプリが表示されます。
初期画面

ダークモード

タスク追加

タスク完了、削除

タイトル変更(即時反映)
タイトルを変更します。以下の内容に変更してください。
src/App.js
<div className={`App ${isDarkMode ? 'dark-mode' : ''}`}>
<header>
<h1>シンプルToDoリストアプリ</h1>
</header>
すると、自動的にReactアプリがコンパイルされます。
Compiled successfully!
react-app-1 |
react-app-1 | You can now view react-todo-app in the browser.
react-app-1 |
react-app-1 | Local: <http://localhost:3000>
react-app-1 | On Your Network: <http://172.18.0.2:3000>
react-app-1 |
react-app-1 | Note that the development build is not optimized.
react-app-1 | To create a production build, use npm run build.
react-app-1 |
react-app-1 | webpack compiled successfully
画面も即時反映されています。これは便利ですね!

Reactアプリの終了
Docker ComposeでReactアプリを終了します。
docker compose down
コード解説
ToDoリストアプリの学習ポイントを、具体的なソースコードを例示しながら、説明します。
このアプリを通じて、次の4つReactの重要な学習ポイントを学びます。
- 状態管理 (useState):タスクのリストや入力内容を管理。
- リストのレンダリング (map):複数のタスクを画面に表示。
- コンポーネントの再利用:アプリをパーツごとに分けて管理し、再利用可能に。
- フォーム処理:ユーザー入力を取得し、タスクを追加する。
1. 状態管理 (useState)
useState
は、Reactで「状態」を管理するためのフック(関数)です。状態とは、変化するデータのことです。ToDoリストアプリの場合、追加されたタスクや、タスクの完了状態などが「状態」として管理されます。
コード例:
import React, { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([]); // タスクのリストを管理する状態
// 新しいタスクを追加する関数
const addTask = (taskText) => {
setTasks([...tasks, { text: taskText, completed: false }]); // タスクをリストに追加
};
return (
<div>
<h1>ToDoリスト</h1>
<button onClick={() => addTask('新しいタスク')}>タスクを追加</button>
</div>
);
}
useState([])
は、タスクのリストを管理する状態を作成しています。初期値は空のリスト([]
)。setTasks()
は、状態を更新するための関数です。新しいタスクが追加されたときに、この関数でリストを更新します。
2. リストのレンダリング (map)
複数のタスクを画面に表示するためには、リストの中身を一つずつ取り出して表示する必要があります。これを「リストのレンダリング」と呼び、map()
というJavaScriptの関数を使います。
コード例:
function TaskList({ tasks }) {
return (
<ul>
{tasks.map((task, index) => (
<li key={index}>{task.text}</li>
))}
</ul>
);
}
tasks.map()
を使って、タスクリスト(tasks
)のそれぞれのタスク(task
)を取り出し、リスト項目(<li>
)として画面に表示しています。key={index}
という部分は、Reactにリストの中身が一意であることを教えるために使います。これにより、Reactはリストを効率的に更新できます。
3. コンポーネントの再利用
Reactでは、画面の一部を「コンポーネント」という単位に分けて、再利用することができます。たとえば、タスク入力フォームやタスクリストの表示を、それぞれ別のコンポーネントに分けて管理します。
コード例:
TaskForm.js
(タスク入力用フォーム)
import React, { useState } from 'react';
function TaskForm({ onAddTask }) {
const [taskText, setTaskText] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); // フォームの自動送信を防ぐ
if (taskText.trim() === '') return;
onAddTask(taskText); // タスクを追加
setTaskText(''); // フォームをクリア
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={taskText}
onChange={(e) => setTaskText(e.target.value)}
placeholder="タスクを入力"
/>
<button type="submit">追加</button>
</form>
);
}
export default TaskForm;
App.js
import React, { useState } from 'react';
import TaskForm from './TaskForm';
import TaskList from './TaskList';
function App() {
const [tasks, setTasks] = useState([]);
const addTask = (taskText) => {
setTasks([...tasks, { text: taskText, completed: false }]);
};
return (
<div>
<h1>ToDoリスト</h1>
<TaskForm onAddTask={addTask} />
<TaskList tasks={tasks} />
</div>
);
}
export default App;
TaskForm
コンポーネントは、タスクを追加するためのフォーム部分を管理します。このコンポーネントは他のアプリでも再利用できるように独立しています。App
コンポーネントは、TaskForm
とTaskList
を組み合わせてToDoリストアプリ全体を作っています。
4. フォーム処理
Reactでは、フォームのデータを管理するために状態管理を使います。たとえば、入力フォームの内容をリアルタイムに取得し、ボタンが押されたときにデータを送信します。
コード例:
import React, { useState } from 'react';
function TaskForm({ onAddTask }) {
const [taskText, setTaskText] = useState('');
const handleChange = (e) => {
setTaskText(e.target.value); // 入力内容をリアルタイムで状態に保存
};
const handleSubmit = (e) => {
e.preventDefault(); // フォームのデフォルト送信を防ぐ
if (taskText.trim()) {
onAddTask(taskText); // 新しいタスクを追加
setTaskText(''); // 入力欄をクリア
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={taskText}
onChange={handleChange}
placeholder="タスクを入力"
/>
<button type="submit">追加</button>
</form>
);
}
export default TaskForm;
setTaskText(e.target.value)
で、入力フォームに書かれた内容をリアルタイムで状態に保存します。handleSubmit
関数では、フォームの送信を防いだ後に、タスクを追加し、フォームをクリアします。
まとめ
これからもReactとmacOS、Dockerコンテナを活用して、より高度なアプリケーション開発に挑戦してみてください!
最後に、この記事がReactの学習やアプリ開発に役立つことを願っています。