はじめに
React16.8で追加されたフックを使ってTodoアプリを作ります.
フックを使うとクラスコンポーネントをファンクショナルコンポーネントに書き換えることができ, 綺麗なコードにすることができます.
Hookについて[公式]
https://ja.reactjs.org/docs/hooks-intro.html
導入
以下の環境です.
- node v12.8.0
- yarn v1.21.0
- crate-react-app v3.3.0
プロジェクトの作成
$ create-react-app project_name
マテリアルUIの導入
$ yarn add @material-ui/core
完成品URL
https://fukekazki.github.io/React-Todo-App/\ リポジトリ
https://github.com/FukeKazki/React-Todo-App
コード解説
App.jsの処理部分を解説します.
import React, { useState } from "react";
import {
Box,
Button,
Checkbox,
Container,
CssBaseline,
List,
ListItem,
ListItemText,
TextField,
} from "@material-ui/core";
const INITIAL_TASK = {
title: "Reactのお勉強",
doing: false,
};
const App = () => {
const [tasks, setTasks] = useState([INITIAL_TASK]);
const [task_title, setTask_title] = useState("");
const handleTextFieldChanges = (e) => {
setTask_title(e.target.value);
};
const resetTextField = () => {
setTask_title("");
};
const isTaskInclude = () => {
return tasks.some((task) => task.title === task_title);
};
const addTask = () => {
setTasks([...tasks, {
title: task_title,
doing: false,
}]);
resetTextField();
};
const deleteTask = (task) => {
setTasks(tasks.filter((x) => x !== task));
};
const handleCheckboxChanges = (task) => {
setTasks(tasks.filter((x) => {
if (x === task) x.doing = !x.doing;
return x;
}));
};
return (
<React.Fragment>
<Container component="main" maxWidth="xs">
<CssBaseline />
<Box
mt={5}
display="flex"
justifyContent="space-around"
>
<TextField
label="タイトル"
value={task_title}
onChange={handleTextFieldChanges}
/>
<Button
disabled={task_title === "" || isTaskInclude()}
variant="contained"
color="primary"
onClick={addTask}
href=""
>
作成
</Button>
</Box>
<List
style={{ marginTop: "48px" }}
component="ul"
>
{tasks.map((task) => (
<ListItem key={task.title} component="li">
<Checkbox
checked={task.doing}
value="primary"
onChange={() => handleCheckboxChanges(task)}
/>
<ListItemText>{task.title}</ListItemText>
<Button
href=""
onClick={() => deleteTask(task)}
>
削除
</Button>
</ListItem>
))}
</List>
</Container>
</React.Fragment>
);
};
export default App;
タスクの持つ状態
17-28行で定義しています.
titleとチェックされているかどうかを判別するためにdoingをもたせました.
const INITIAL_TASK = {
title: "Reactのお勉強",
doing: false,
};
stateについて
23-24行で定義しています.
useStateの引数には初期値を渡しています.
左辺は分割代入を使って0番目がstate, 1番目がいままでのsetState関数になります.
const [tasks, setTasks] = useState([INITIAL_TASK]);
const [task_title, setTask_title] = useState("");
タスクの追加
38-44行です.
setTasks関数の引数にスプレッド演算子を用いて展開と追加をしています.
const addTask = () => {
setTasks([...tasks, {
title: task_title,
doing: false,
}]);
resetTextField();
};
タスクの削除
46-48行です.
filterを用いて削除を実装しています.
const deleteTask = (task) => {
setTasks(tasks.filter((x) => x !== task));
};
タスクの検索
34-36行です.
同じタスク名のものを追加できないようにそのタスクがすでに存在しているかどうかを検索します.
someを用いると条件式に一致するものがあるかどうかを真偽値で返してくれます.
const isTaskInclude = () => {
return tasks.some((task) => task.title === task_title);
};
タスクの表示
78-97行です.
Material UIのListを用いています.
mapを使ってtasks配列内の値を表示しています.
<List
style={{ marginTop: "48px" }}
component="ul"
>
{tasks.map((task) => (
<ListItem key={task.title} component="li">
<Checkbox
checked={task.doing}
value="primary"
onChange={() => handleCheckboxChanges(task)}
/>
<ListItemText>{task.title}</ListItemText>
<Button
href=""
onClick={() => deleteTask(task)}
>
削除
</Button>
</ListItem>
))}
</List>;
フォーム
63-76行です.
onChangeメソッドで入力が変化するたびにhandleTextFieldChanges関数を実行しています.
Buttonのdisabledは 押せるか押せないか を表すもので trueのときは押せません.
タスクタイトルが空もしくは, おなじタスク名のものが既にある既にある場合はtrueになります.
<TextField
label='タイトル'
value={task_title}
onChange={handleTextFieldChanges}
/>
<Button
disabled={task_title === '' || isTaskInclude()}
variant='contained'
color='primary'
onClick={addTask}
href=''
>
作成
</Button>
おわり
Hookを使うとコードがきれいになってうれしい!!
みんなHookつかってね♡