🌍

React(ts) で Todoアプリを作ってみた

2023/10/11に公開

react setup

npx create-react-app react-todo-ts --template typescript 
cd react-todo-ts
yarn start 

todo アプリ作成

まず、最初にtypes.tsを作成
intertfaceを用いて'Todo'という型を定義しています。
interface: 新しい方を定義する。(Todo)

export interface Todo{
  id: number;
  text: string;
  completed: boolean;
}

export default Todo;

App.tsx

まずApp.tsxにtodoの処理や画面を書きます。

import React, { useState } from 'react';
import './App.css';
import {Todo} from './types';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
const App: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([
    { id: 1, text: 'Learn React with TypeScript', completed: false },
  ])
  const addTodo = (text: string) => {
    setTodos([...todos, { id: todos.length + 1, text, completed: false }]);
  };

  const toggleTodo = (id: number) => {
    setTodos(
      todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };
  return (
    <div className="App">
      <h1>Todo App</h1>
      <AddTodo addTodo={addTodo} />
      <TodoList todos={todos} toggleTodo={toggleTodo} />
    </div>
  );
}

export default App;

const [todos, setTodos] = useState<Todo[]>([
   { id: 1, text: 'Learn React with TypeScript', completed: false },
])

Reactのフックである、ローカルステートを管理するために使用します。
また、Todo[]はTypeScriptのジェネリクスでtodo型がtodoオブジェクトの配列と指定

const toggleTodo = (id: number) => {
   setTodos(
     todos.map(todo =>
       todo.id === id ? { ...todo, completed: !todo.completed } : todo
     )
   );
 };

こちらはcheckboxがクリックされた時に反応する関数です。
クリックされたtodoをmap関数で一致するidを探す、見つけたcompletedを反転させます。

次にレンダリングされてるAddTodo.tsxを作成します。

AddTodo.tsx

// AddTodo.tsx
import React, { useState, FC } from 'react';

interface AddTodoProps {
addTodo: (text: string) => void;
}

const AddTodo: FC<AddTodoProps> = ({ addTodo }) => {
const [text, setText] = useState('');

const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault();
  addTodo(text);
  setText('');
};

return (
  <form onSubmit={handleSubmit}>
    <input
      type="text"
      value={text}
      onChange={e => setText(e.currentTarget.value)}
    />
    <button type="submit">Add Todo</button>
  </form>
);
};

export default AddTodo;
 interface AddTodoProps {
 addTodo: (text: string) => void;
}

AddTodoコンポーネントに渡されるpropsの型を定義します。
この場合はstringになります。

const AddTodo: FC<AddTodoProps> = ({ addTodo }) => {
};

FC<AddTodoProps>でAddTodoという名前の関数コンポーネントを定義しpropsの型が先ほど宣言したInteferfaceとしています。
{{add Todo}}ではデストラクチャリングを使用して、propsからaddTodo関数を抽出する。

	return (
  <form onSubmit={handleSubmit}>
    <input
      type="text"
      value={text}
      onChange={e => setText(e.currentTarget.value)}
    />
    <button type="submit">Add Todo</button>
  </form>
);

formで送信するとhandleSubmit関数が呼び出されます。

次にTodoList.tsx になります。

TodoList.tsx

	// TodoList.tsx
import React, { FC } from 'react';
import TodoItem from './TodoItem';
import { Todo } from './types';

interface TodoListProps {
  todos: Todo[];
  toggleTodo: (id: number) => void;
}

const TodoList: FC<TodoListProps> = ({ todos, toggleTodo }) => {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} {...todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
};

export default TodoList;

AddTodo.tsxと同じようにInterfaceを作ります。

	interface TodoListProps {
  todos: Todo[];
  toggleTodo: (id: number) => void;
}

	```
AddTodoの方で解説してるので、ここは省きます。
次にコンポーネントになります。
	```
	const TodoList: FC<TodoListProps> = ({ todos, toggleTodo }) => {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} {...todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
};

ここはulの要素の中でtodos配列をmapでループしています。
各ループでTodoItemコンポーネントをレンダリングしてその各todoオブジェクトをpropsとして渡します。

TodoItem.tsx

// TodoItem.tsx
import React, { FC } from 'react';
import { Todo } from './types';

interface TodoItemProps extends Todo {
  toggleTodo: (id: number) => void;
}

const TodoItem: FC<TodoItemProps> = ({ id, text, completed, toggleTodo }) => {
  return (
    <li>
      <input
        type="checkbox"
        checked={completed}
        onChange={() => toggleTodo(id)}
      />
      {text}
    </li>
  );
};

export default TodoItem;

Discussion