【React】Todoアプリ

todoアプリ作成

参考サイト:
https://www.youtube.com/watch?v=nRCNL9T3J98

ノードjsインストール確認、Reactひな型作成

PS C:\Users\…\react-project> node -v
v18.17.1
PS C:\Users\…\react-project> npx create-react-app react-tutorial  

→完了するとHappy hacking!の表示

不要なファイルの削除\src\App.test.js src\logo.svg \src\reportWebVitals.js \src\setupTests.js

コンポーネントの作成

▼\src\TodoList.jsを作成

VSCodeプラグインES7+ React/Redux/React-Native をインストールしていればrafceタブ補完と入力すれば下記のように関数コンポーネントが入力されます

import React from 'react'
export const TodoList = () => {
  return (
    <div>TodoList</div>
  )
}

▼App.jsを編集

インポートの記載

import { TodoList } from "./TodoList";
function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}
export default App;

入力欄、ボタン作成

▼.js

import TodoList from "./TodoList";

function App() {
  return (
    <div className="App">
      <div>
        <TodoList />
        <input type="text" />
        <button>タスクを追加</button>
        <button>完了したタスクの削除</button>
        <div>残りのタスク:0</div>
      </div>
    </div>
  );
}

export default App;

1)useStateで状態を監視→変更で再レンダリング
2)propsでtodos={todos}でコンポーネント間のデータの受け渡し

▼App.js

import { useState } from "react";
import TodoList from "./TodoList";

function App() {
  const [todos, setTodos] = useState(["Todo1", "Todo2"]);

  return (
    <div className="App">
      <div>
        <TodoList todos={todos}/>
        <input type="text" />
        <button>タスクを追加</button>
        <button>完了したタスクの削除</button>
        <div>残りのタスク:0</div>
      </div>
    </div>
  );
}

export default App;

useState()

useState()は、関数コンポーネントでstateを管理(stateの保持と更新)

stateとはコンポーネントが内部で保持する「状態」

▼TodoList.js

import React from 'react'

const TodoList = ({ todos }) => {
  return (
    <div>{todos}</div>
  )
}

export default TodoList

Todo.jsを作成(各タスクをコンポーネントにおきかえる)

▼TodoList.js

import React from 'react';
import Todo from './Todo';

const TodoList = ({ todos }) => {
  return todos.map((todo) => <Todo todo={todo} />);
}

export default TodoList

▼Todo.js

import React from 'react'

const Todo = ({ todo }) => {
  return (
    <div>{todo}</div>
  )
}

export default Todo

todoをオブジェクトとして管理

▼App.js

function App() {
  const [todos, setTodos] = useState([
    { id: 1, name: "Todo1", completed: false },
  ]);

▼Todo.js

const Todo = ({ todo }) => {
  return (
    <div>{todo.name}</div>
  )
}

チェックボックスの実装(チェックオンオフは後から実装)

▼Todo.js

    <div>
        <label>
            <input type="checkbox" checked={todo.completed} readOnly />
        </label>
        {todo.name}
    </div>

タスクの追加 
useRefで要素の取得(オブジェクトにおけるスプレッド構文の追加)

▼useRefで取得した内容をconsole.log()で確認

▼App.js

import { useState, useRef } from "react";
import TodoList from "./TodoList";

function App() {
  const [todos, setTodos] = useState([
    { id: 1, name: "Todo1", completed: false },
  ]);

  // useRefでinputの値を取得
  const todoNameRef = useRef();

  // タスクを追加する
  const handleAddTodo = () => {

    const name = todoNameRef.current.value;

    // 空白のタスクを生成しない
    if(name === "") return;

    // setTodosでTodosの内容を更新
    // オブジェクトにおけるスプレッド構文の追加
    setTodos((prevTodos) => {
      return [...prevTodos, { id: "1", name: name, completed: false }];
    });

    todoNameRef.current.value = null;
  };

  return (
    <div className="App">
      <div>
        <TodoList todos={todos}/>
        <input type="text" ref={todoNameRef} />
        <button onClick={handleAddTodo}>タスクを追加</button>
        <button>完了したタスクの削除</button>
        <div>残りのタスク:0</div>
      </div>
    </div>
  );
}

export default App;

対応)Warning: Each child in a list should have a unique “key” prop.

uuidインストール

ユニークキーの生成が可能

https://www.npmjs.com/package/uuid

npm install uuid→package.jsonにてインストールの確認ができます

  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "uuid": "^9.0.1",
    "web-vitals": "^2.1.4"
  },

▼App.jsを編集

import { useState, useRef } from "react";
import { TodoList } from "./TodoList";
import { v4 as uuidv4 } from 'uuid';

function App() {
  const [todos, setTodos] = useState([
    { id: 1, name: "Todo1", competed: false }
  ]);

  const todoNameRef = useRef();

  const handleAddTodo = () => {
    // タスクを追加
    const name = todoNameRef.current.value;
    setTodos((prevTodos) => {
      return [...prevTodos, { id: uuidv4(), name: name, completed: false }]
    });
    todoNameRef.current.value = null;
  };

▼TodoList.jsを編集

export const TodoList = ({ todos }) => {
  return todos.map((todo) => <Todo todo={todo} key={todo.id}/>);
}

初期値をから行列にApp.js

function App() {
  const [todos, setTodos] = useState([]);

toggleTodoでチェックボックスを管理

▼App.jsを編集


  // チェックボックスの操作
  const toggleTodo = (id) => {

    // 直接値を変更しない為newTodosにコピーしている
    const newTodos = [...todos];

    // 引数のidとtodoのidが一致したら、completedを反転
    const todo = newTodos.find((todo) => todo.id === id);
    todo.completed = !todo.completed;
    setTodos(newTodos);
  };

  return (
    <div className="App">
      <div>
        <TodoList todos={todos} toggleTodo={toggleTodo} />
        <input type="text" ref={todoNameRef} />
        <button onClick={handleAddTodo}>タスクを追加</button>
        <button>完了したタスクの削除</button>
        <div>残りのタスク:0</div>
      </div>
    </div>
  );
}

export default App;

▼TodoList.jsを編集


const TodoList = ({ todos, toggleTodo }) => {
  return todos.map((todo) => <Todo todo={todo} key={todo.id} toggleTodo={toggleTodo} />);
}

▼Todo.jsを編集

import React from 'react'

const Todo = ({ todo, toggleTodo }) => {

    const handleTodoClick = () => {
        toggleTodo(todo.id);
    };

    return (
        <div>
            <label>
                <input 
                    type="checkbox" 
                    checked={todo.completed} 
                    readOnly 
                    onClick={handleTodoClick} 
                />
            </label>
            {todo.name}
        </div>
    )
}

export default Todo

残りのタスクの数をいれる

▼App.jsを編集


    <div className="App">
      <div>
        <TodoList todos={todos} toggleTodo={toggleTodo} />
        <input type="text" ref={todoNameRef} />
        <button onClick={handleAddTodo}>タスクを追加</button>
        <button>完了したタスクの削除</button>
        <div>残りのタスク:{todos.filter((todo) => !todo.completed).length}</div>
      </div>
    </div>

完了したタスクの削除

  // 完了したタスクの削除
  const handleClear = () => {
    const newTodos = todos.filter((todo) => !todo.completed);
    setTodos(newTodos);
  }

  return (
    <div className="App">
      <div>
        <TodoList todos={todos} toggleTodo={toggleTodo} />
        <input type="text" ref={todoNameRef} />
        <button onClick={handleAddTodo}>タスクを追加</button>
        <div>残りのタスク:{todos.filter((todo) => !todo.completed).length}</div>
        <button onClick={handleClear}>完了したタスクの削除</button>
      </div>
    </div>
  );
}

レンタルサーバで公開

参考サイト:https://zenn.dev/kiriyama/articles/538face511307d

▼package.jsonを編集

▼\public.htaccessを作成(publickフォルダに「.htaccess」のファイルを作成)

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

▼プロジェクトディレクトリで下記のコマンドでビルド

npm run build

buildというフォルダが作成されるので、その中身をFTPでアップロード

アップロードしたサイト:https://internet.mints.ne.jp/react-todo/


https://www.youtube.com/watch?list=PLX8Rsrpnn3IWPoM7-1YPDksRRkamRY25k&time_continue=8&v=XKSYF2aZnkQ&embeds_referring_euri=https%3A%2F%2Fsbucks-blog.com%2F&source_ve_path=Mjg2NjY&feature=emb_logo