Reactの復習もかねてTodoアプリを作成しました!
学んだことをまとめてみたいと思います。
動作環境
エディション | Windows 11 Home |
バージョン | 21H2 |
Node | 18.15.0 |
開発環境
React
「Reactを最低限の構成で動かしてみる」で作成したひな型を使います。
CSSフレームワーク
WebページのデザインにMaterial-UIを使用
Material UI は、Google のMaterial Designを実装するオープンソースの React コンポーネント ライブラリです。
https://m2.material.io/
あらかじめデザインされたコンポーネントを使うことで簡単にWebページの見た目を整えることができます。複数で開発する際、デザインを統一できるメリットもあります。
Material-UI
ユーザーインターフェース設計
ユーザーインターフェース設計にFigmaを使用
無料で使用できるユーザーインターフェース設計ツール。Material-UIのテンプレートも用意されているので開発時にほぼ同じデザインでWebページを作成できる。
Figma
おすすめのFigmaテンプレート
①Material 2 Design Kit
基本的なコンポーネントデザインとワイヤーフレームが用意されています。今回は使いませんでしたが、もしMaterial-UIを使う場合はとても役に立つ。
②Material Design Icons
様々な視覚的にわかりやすいiconが用意されています。今回は追加と削除ボタンに使用しました。
機能設計
追加ボタンを押すとフォームに入力された値と削除ボタンが画面に表示される。
削除ボタンを押すとフォームに入力された値と削除ボタンが削除される。
追加ボタンで追加したフォームの値を編集できる。
ワイヤーフレーム
Figmaで作成したユーザーインターフェースを使ってTodoアプリを作成しました。
+(追加)ボタンを押すとTodoリストに入力値が追加され一覧表示されるイメージ。
一覧表示されたTodoリストには編集と削除の機能がある。
Todoアプリの構成
3つのコンポーネントを新しく作成しました。
TodoPage、TodoForm、TodoList、各コンポーネントの役割は以下となります。
TodoPageはTodoFormとTodoListをレンダリングする親コンポーネントの役割をします。
TodoFormは値を入力するフォームと追加ボタンを定義して親コンポーネント(TodoPage)に返却します。
TodoListはTodoFormで入力した値を表示させるフォームと削除ボタンを定義して、親コンポーネント(TodoPage)に返却します。
詳細はコンポーネントの説明にて説明します。
src
│ index.jsx
│
├─components
│ TodoForm.jsx
│ TodoList.jsx
│
└─pages
TodoPage.jsx
コンポーネントの説明
index.jsx
画面にTodoPageコンポーネントをレンダリングしています。
import React from 'react';
import ReactDOM from 'react-dom/client';
import { TodoPage } from './pages/TodoPage';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<TodoPage />);
TodoPage.jsx
親コンポーネントのTodoPageに定義した内容を説明します。
const [todo, setTodo] = useState('');
const [todoList, setTodoList] = useState([]);
まずは親のコンポーネントにフォームから入力した値を保持するためreact hookのuseStateで状態保持変数(todo,todoList)と状態更新関数(setTodo,setTodoList)を定義しています。
フォームから入力された値(todo)を配列(todoList)に格納していきます。
<TodoForm todo={todo} setTodo={setTodo} todoList={todoList} setTodoList={setTodoList} />
TodoFormコンポーネントに状態保持変数(todo,todoList)と状態更新関数(setTodo,setTodoList)をprops(引数)として渡します。渡されたpropsはフォームから入力された値を配列(todoList)に追加するために使用します。
<TodoList todoList={todoList} setTodoList={setTodoList} />
TodoListコンポーネントに状態保持変数(todoList)と状態更新関数(setTodoList)をprops(引数)として渡します。渡されたpropsは配列(todoList)の中身を参照・更新・削除するために使用します。
最終的に親のコンポーネント(TodoPage)のレンダリング結果を画面に返却します。
import { useState } from 'react';
import { Container, Box } from '@mui/material';
import { TodoForm } from '../components/TodoForm';
import { TodoList } from '../components/TodoList';
export const TodoPage = () => {
const [todo, setTodo] = useState('');
const [todoList, setTodoList] = useState([]);
return (
<Box>
<Container maxWidth='xs'>
<h1>TODO LIST</h1>
</Container>
<TodoForm todo={todo} setTodo={setTodo} todoList={todoList} setTodoList={setTodoList} />
<TodoList todoList={todoList} setTodoList={setTodoList} />
</Box>
);
};
TodoForm.jsx
子コンポーネントのTodoFormに定義した内容を説明します。
setTodo(e.target.value);
onChangeイベントは親のコンポーネントから渡された状態更新関数(setTodo)でフォームに入力された値を更新します。
setTodoList([...todoList, todo]);
スプレッド構文にてtodoListとtodoをマージしています。
onClickイベントは親のコンポーネントから渡された状態更新関数(setTodoList)でフォームに入力された値を配列(todoList)に追加後、更新します。
これらの更新結果をもとにTodoListコンポーネント内で状態保持変数(todoList)に格納された値の数だけ入力フォームと削除ボタンを作成していきます。
import { Container, Box, TextField, IconButton } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
export const TodoForm = (props) => {
const { todo, setTodo, todoList, setTodoList } = props;
return (
<Container maxWidth='xs'>
<TextField
id='inputTodo'
label=''
type='text'
name='inputTodo'
value={todo}
onChange={(e) => {
console.log('inputTodo:onChange');
setTodo(e.target.value);
}}
variant='outlined'
/>
<IconButton
aria-label='add'
size='large'
type='submit'
onClick={() => {
console.log('addButton:onClick');
setTodoList([...todoList, todo]);
}}
>
<AddIcon fontSize='inherit' />
</IconButton>
</Container>
);
};
TodoList.jsx
子コンポーネントのTodoListに定義した内容を説明します。
map関数を使って親のコンポーネントから渡された状態保持変数(todoList)の値の数だけ入力フォームと削除ボタンを作成して、親のコンポーネント(TodoPage)に返却します。
div要素に一意であるkeyを指定することで無駄な再レンダリングせずに済みます。keyを指定しないと同じ値があった場合、どちらの値が更新されたか識別ができなくなるため全て再レンダリングを行うことになります。
更新処理はmap関数、削除処理はfilter関数を使います。map、filter関数の引数として定義したインデックス番号とコンポーネント生成時のインデックス番号の差異によって更新と削除を行う条件としています。
setTodoList(todoList.map((todo, i) => (index === i ? e.target.value : todo))
onChangeイベントで親のコンポーネントから渡された状態保持変数(todoList)の値をフォームから入力した値に更新します。更新結果を状態更新関数(setTodoList)で更新します。
setTodoList(todoList.filter((todo, i) => index !== i));
onClickイベントで親のコンポーネントから渡された状態保持変数(todoList)の値を削除します。削除結果を状態更新関数(setTodoList)で更新します。
import { Container, Box, TextField, IconButton } from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
export const TodoList = (props) => {
const { todoList, setTodoList } = props;
return (
<Container maxWidth='xs'>
{todoList.map((todo, index) => {
return (
<div key={index}>
<TextField
id='outputTodo'
label=''
name='outputTodo'
value={todo}
onChange={(e) => {
console.log('TodoList:onChange');
setTodoList(
todoList.map((todo, i) => (index === i ? e.target.value : todo))
);
}}
variant='standard'
/>
<IconButton
aria-label='delete'
size='large'
type='submit'
onClick={() => {
console.log('deleteButton:onClick');
setTodoList(todoList.filter((todo, i) => index !== i));
}}
>
<DeleteIcon fontSize='inherit' />
</IconButton>
</div>
);
})}
</Container>
);
};
完成したTodoアプリ
Todoアプリの追加、編集、削除のデモとなります!
今回学んだこと
ReactのCompornent定義と分割の仕方、propsでのuseStateの受け渡し
Reactの仮想DOM、レンダリング、hook 状態管理(useState)
Reactでのイベント定義、CRUD処理(mapやfilter)
Figmaを使ったUI設計
Material-UIでのデザイン
おすすめの書籍
JavaScriptのおすすめの書籍です。
おすすめのUdemy講座
ReactでおすすめのUdemy講座を紹介します。
モダンJavaScriptの基礎から始める挫折しないためのReact入門
ReactはJavaScriptの基本的な構文を使用するため、Reactだけの理解では足りないでしょう。
こちらのReact講座は、JavaScriptでのTODOアプリを作成してからReactでのTODOアプリを作成するため、ReactだけではなくJavaScriptを基礎から学ぶことができます。
Reactからいきなり入らずにJavaScriptの基礎から学べる点が、初心者にとてもおすすめできる内容となっています。
【Reactアプリ開発】3種類のReactアプリケーションを構築して、Reactの理解をさらに深めるステップアップ講座
APIを使用してデータを取得したり、react-router-domでのページ遷移を学べるためより実践的な内容となっています。
3種類のReactアプリケーションを作成することができるため、Reactの基礎的な内容を学んだ人におすすめです。
基本的に構文が載っているため、教科書代わりに使える内容となっています。
Reactの前にとてもお世話になりました。JavaScriptをこれから始める方におすすめです。