はじめに
メモ替わりとしての投稿なのでちょっと雑かも笑
完成系
今回はあくまでCSSを学習するためではないので殆どいじっていないです。(見た目はダサいですが…)
暇な時があればCSSを変更しようかなと思います笑
機能
- タスク追加
- タスクの状態選択
- タスク削除
機能は至ってシンプルで上の3つを動作させようと思います。
1. Reactのプロジェクトを作成
npx create-react-app 'todo-app'
npm start
ディレクトリ構造は今回はこんな感じにしました↓
- node_modules
- src
|- components
|- components
|- TodoList.jsx
|- App.jsx
|- index.js
-package-lock.json
-package-json
src直下にcomponentsディレクトリを作成して、その中にTodoList.jsxファイルを入れました。App.jsxだけで完結させようかとも思いましたが、propsの渡し方等も学ぼうと思ったのでTodo追加部分をTodoList.jsxにまとめました。
今回使うのはTodoList.jsxとApp.jsxのみです!それではいきましょ〜ー!!
2. Todoリストの機能追加前の準備
1. Todoリスト雛形作る
import './App.css';
import TodoList from './components/TodoList'
function App() {
return (
<>
<h1>Todoリスト</h1>
<p>残りのタスク:0</p>
<input />
<button>追加</button>
<button>削除</button>
<TodoList />
</>
)
}
export default App;
import React from 'react';
const TodoList = () => {
return (
<div>
<label>
<input
type='checkbox'
/>
todo1
</label>
</div>
)
}
export default TodoList;
App.jsxでTodoListをインポートしてはTodoList.jsxのconst TodoList = ...(以下省略)を呼び出しています。
2. タスクの状態を管理する
//...省略
import {useState} from 'react';
function App() {
const [todos, setTodos] = useState([
{id: 1, name: 'todo1', completed: false}
])
//...省略
ReactフックのuseStateを使ってタスクの状態を管理します。{id: 1, name: 'todo1', completed: false}これは初期値です。completed: falseについてはTodoの完了か未完了かの状態を表しています。←後で使います。値を更新する際は、setTodosを使います。
3. 初期値を表示してみる
//...省略
<button>削除</button>
<TodoList todos={todos} />
</>
//...省略
//...省略
const TodoList = ({ todos }) => {
return (
todos.map(todo => {
return (
<div key={todo.id}>
<label>
<input
type="checkbox"
/>
{todo.name}
</label>
</div>
)
})
)
}
//...省略
App.jsxでTodoList.jsxにpropsを渡します。TodoList.jsxで渡されたtodosをmapメソッドを使って1つずつ取り出してtodo.nameを記載して初期値を表示させます。また、keyを入れないとwarningが出るので入れます。
こうなったらok↓
これで準備が整ったので次はTodoの機能を作っていきましょう!
3. タスクの追加
1. 入力した値を取得出来るか確認する
//...省略
const [todos, setTodos] = useState([
{id: 1, name: 'todo1', completed: false}
])
//追加
const [inputValue, setInputValue] = useState('')
const handleInputValue = (e) => {
setInputValue(e.target.value)
}
const handleAddTodo = () => {
console.log(inputValue)
}
return (
<>
<h1>Todoリスト</h1>
<p>残りのタスク:0</p>
//追加
<input value={inputValue} onChange={handleInputValue} />
<button onClick={handleAddTodo}>追加</button>
//...省略
inputに適当な文字を入力して追加ボタンをクリックした時に値がconsoleに出力されればok!
2. 入力したタスクを追加する
値が取得出来たかどうかを確認できたら、タスク追加機能を作成する。
const handleInputValue = (e) => {
setInputValue(e.target.value)
}
//追加
const handleAddTodo = () => {
if(inputValue === '') return
setTodos((prevTodos) => {
return [...prevTodos, {id: uuidv4(), name: inputValue, completed: false}]
})
setInputValue('')
}
//ここまで
先ほど値を取得する際に書いたコードを値を追加出来るように書き換えます。
handleAddTodoの部分について
if(inputValue === '') return
これは値が空の場合はtodoが追加出来ないようになっています。
setTodos((prevTodos) => {
return [...prevTodos, {id: uuidv4(), name: inputValue, completed: false}]
})
...prevTodosの部分: 既存のtodosの全要素を展開して、新しい配列にコピーしています。
{id: uuidv4(), name: inputValue, completed: false}の部分: 新しいTodoオブジェクトを作成しています。
イメージ的には{id: uuidv4(), name: inputValue, completed: false}が作成されて...prevTodosに追加されていくっていう感じ?かなぁ?言葉にするのって難しい。。。
id: uuidv4()の部分: これはuuidを使っており、重複していないidを生成してくれます。詳しい使い方はこちら→https://www.npmjs.com/package/uuid
これでタスク追加機能は出来ました。
ここまでのソースコードはこちら↓
import './App.css';
import TodoList from './components/TodoList';
import {useState} from 'react';
import { v4 as uuidv4 } from 'uuid';
function App() {
const [todos, setTodos] = useState([
{id: 1, name: 'todo1', completed: false}
])
const [inputValue, setInputValue] = useState('')
const handleInputValue = (e) => {
setInputValue(e.target.value)
}
const handleAddTodo = () => {
if(inputValue === '') return
setTodos((prevTodos) => {
return [...prevTodos, {id: uuidv4(), name: inputValue, completed: false}]
})
setInputValue('')
}
return (
<>
<h1>Todoリスト</h1>
<p>残りのタスク:0</p>
<input value={inputValue} onChange={handleInputValue} />
<button onClick={handleAddTodo}>追加</button>
<button>削除</button>
<TodoList todos={todos} />
</>
)
}
export default App;
import React from 'react';
const TodoList = ({ todos }) => {
return (
todos.map(todo => {
return (
<div key={todo.id}>
<label>
<input
type='checkbox'
/>
{todo.name}
</label>
</div>
)
})
)
}
export default TodoList;
3. タスクの状態選択機能作成
//...省略
setInputValue('')
}
//追加
const todoCompleted = (id) => {
const newTodos = [...todos]
const checkedTodo = newTodos.find((newTodo) => newTodo.id === id)
checkedTodo.completed = !checkedTodo.completed
setTodos(newTodos)
}
//ここまで
return (
//...省略
export default App;
import React from 'react';
const TodoList = ({ todos, todoCompleted }) => {
return (
todos.map(todo => {
//追加
const handleTodoClick = () => {
todoCompleted(todo.id)
}
//ここまで
return (
<div key={todo.id}>
<label>
<input
type='checkbox'
//追加
checked={todo.completed}
onChange={handleTodoClick}
//ここまで
/>
{todo.name}
</label>
</div>
)
})
)
}
export default TodoList;
App.js const todoCompleted = ...(以下省略)について
const newTodos = [...todos]
...todosをコピーしています。
何でコピーするのか、以下参考に↓
https://ja.react.dev/learn/updating-arrays-in-state
const checkedTodo = newTodos.find((newTodo) => newTodo.id === id)
コピーしたtodoとチェックしたtodoが一致したものをfindメソッドで探します。findメソッドは条件に一致した最初の値を取得することができます。
checkedTodo.completed = !checkedTodo.completed
findメソッドで取得したcheckedTodo.completedを反転させることによってチェックボックスのクリックを実現させています。
setTodos(newTodos)
この記述によりチェックボックスの状態を更新できます。
4. タスク削除
//...省略
setTodos(newTodos)
}
//追加
const handleClear = () => {
setTodos(todos.filter(todo => !todo.completed))
}
//ここまで
return (
//...省略
ここではfilterメソッドを使っています。filterメソッドは条件に一致したもの、つまりtrueを返す要素のみを新しい配列に残します。
① todo.completedがtrueの場合(チェックあり):!todo.completedはfalse
② todo.completedがfalseの場合(チェックなし):!todo.completedはtrue
filterメソッドはtrueを返す要素のみを残すので①は消えて②は残ります。つまり、チェックがついたものを削除できる ということです!
ここの理解がムズかった、、、
5. 残りのタスクの数を表示する
//...省略
return (
<>
<h1>Todoリスト</h1>
//追加
<p>残りのタスク:{todos.filter(todo => !todo.completed).length}</p>
//ここまで
<input value={inputValue} onChange={handleInputValue} />
//...省略
残りのタスク:{todos.filter(todo => !todo.completed).length}
filterでtodo.completedの状態を反転させることによって残タスクを表示することができます。
これで基本機能だけですが、todoリストを完成させることができました!
全ソースコードはこちら↓
import './App.css';
import TodoList from './components/TodoList';
import {useState} from 'react';
import { v4 as uuidv4 } from 'uuid';
function App() {
const [todos, setTodos] = useState([
- {id: 1, name: 'todo1', completed: false}//これ消していいです。
])
const [inputValue, setInputValue] = useState('')
const handleInputValue = (e) => {
setInputValue(e.target.value)
}
const handleAddTodo = () => {
if(inputValue === '') return
setTodos((prevTodos) => {
return [...prevTodos, {id: uuidv4(), name: inputValue, completed: false}]
})
setInputValue('')
}
const todoCompleted = (id) => {
const newTodos = [...todos]
const checkedTodo = newTodos.find((newTodo) => newTodo.id === id)
checkedTodo.completed = !checkedTodo.completed
setTodos(newTodos)
}
const handleClear = () => {
setTodos(todos.filter(todo => !todo.completed))
}
return (
<>
<h1>Todoリスト</h1>
<p>残りのタスク:{todos.filter(todo => !todo.completed).length}</p>
<input value={inputValue} onChange={handleInputValue} />
<button onClick={handleAddTodo}>追加</button>
<button onClick={handleClear}>削除</button>
<TodoList todos={todos} todoCompleted={todoCompleted} />
</>
)
}
export default App;
import React from 'react';
const TodoList = ({ todos, todoCompleted }) => {
return (
todos.map(todo => {
const handleTodoClick = () => {
todoCompleted(todo.id)
}
return (
<div key={todo.id}>
<label>
<input
type='checkbox'
checked={todo.completed}
onChange={handleTodoClick}
/>
{todo.name}
</label>
</div>
)
})
)
}
export default TodoList;
一応githubにも載せてます→https://github.com/kurumi-program/todo-app
最後に
初めてのQiitaでの記事投稿でしたが、コードを言語化して説明文にするのがやっぱり難しかったです。
ですが、改めていい復習になったのでこれからも作成を続けていこうと思います。ほぼ自己満ですが笑