0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React×TypeScriptの環境構築

Posted at

この記事の概要

ReactでTypeScriptを使うための環境構築について記載する。

あくまで勉強のためなので、今回はこちらの記事から
ソース等をほぼコピペしてきただけです。(ちょっと階層を変えました。)
もし、内容が気になる方はこちらの方の記事をご覧ください。
(このような記事はとても助かります。ありがとうございます。)

Reactの環境構築

TypeScriptの環境構築

React×TypeScriptの環境構築

Reactを作成する際にTypeScriptを使う設定ができるため、
Node.jsをインストールして、以下のコマンドを入力すればいい。

// create-react-appのTypeScriptバージョンをインストールする
$ npx create-react-app . --template typescript

軽くアプリを作成して起動してみる

アプリ起動

// アプリ起動
$ npm start

db.jsonの作成(本来APIで取得するようなイメージ)

db.json
{
  "todos": [
    {
      "id": 1,
      "content": "Create react appをインストールする",
      "done": true
    },
    {
      "id": 2,
      "content": "JSON Server仮のAPIを作成する",
      "done": false
    },
    {
      "id": 3,
      "content": "Chakra UIをインストールする",
      "done": false
    }
  ]
}

モックサーバ起動

// モックサーバ起動
$ npx json-server --watch db.json --port 3100

※json-serverのインストールが聞かれるのでインストールします。

必要なもののインストール

// axiosのインストール
$ npm install axios
// ulidのインストール
$ npm install ulid

アプリ作成

App.tsx
import { useRef } from "react";

import { useTodo } from "./hooks/useTodo";
import { Todo } from "./types/Todo";
import { TodoAdd } from "./components/TodoAdd";
import { TodoList } from "./components/TodoList";
import { TodoTitle } from "./components/TodoTitle";

function App() {
  // カスタムフックから必要な変数を取得
  const { todoList, toggleTodoListItemStatus, addTodoListItem, deleteTodoListItem } = useTodo();

  const inputEl = useRef<HTMLTextAreaElement>(null);

  const handleAddTodoListItem = () => {
    if (inputEl.current?.value === "") {
      return;
    }
    addTodoListItem(inputEl.current!.value);
    inputEl.current!.value = "";
  };

  // 未完了リスト
  const incompletedList = todoList.filter((todo: Todo) => !todo.done);
  // 完了リスト
  const completedList = todoList.filter((todo: Todo) => todo.done);

  return (
    <>
      <TodoTitle title="TODO進捗管理" as="h1" />
      <TodoAdd buttonText="+ TODOを追加" inputEl={inputEl} handleAddTodoListItem={handleAddTodoListItem} />
      <TodoList todoList={incompletedList} toggleTodoListItemStatus={toggleTodoListItemStatus} deleteTodoListItem={deleteTodoListItem} title="未完了TODOリスト" as="h2" />
      <TodoList todoList={completedList} toggleTodoListItemStatus={toggleTodoListItemStatus} deleteTodoListItem={deleteTodoListItem} title="完了TODOリスト" as="h2" />
    </>
  );
}

export default App;
components/TodoItem.tsx
import { Todo } from "../types/Todo";

// 1つのTodo、内容と移動・削除ボタン
export const TodoItem = ({ todo, toggleTodoListItemStatus, deleteTodoListItem }: { todo: Todo; toggleTodoListItemStatus: any; deleteTodoListItem: any }) => {
  // onClickイベントが発生したら、useTodoフックを呼び出す
  const handleToggleTodoListItemStatus = () => toggleTodoListItemStatus(todo.id, todo.done);
  const handleDeleteTodoListItem = () => deleteTodoListItem(todo.id);

  return (
    <>
      {todo.content}
      <button onClick={handleToggleTodoListItemStatus}>{todo.done ? "未完了リストへ" : "完了リストへ"}</button>
      <button onClick={handleDeleteTodoListItem}>削除</button>
    </>
  );
};

components/TodoList.tsx
import { TodoTitle } from "./TodoTitle";
import { TodoItem } from "./TodoItem";
import { Todo } from "../types/Todo";

// TodoItemをループして表示
// todoListが0件の場合、タイトルとTODOリストを表示しない
export const TodoList = ({
  todoList,
  toggleTodoListItemStatus,
  deleteTodoListItem,
  title,
  as,
}: {
  todoList: Todo[];
  toggleTodoListItemStatus: (id: string, status: boolean) => void;
  deleteTodoListItem: (id: string) => void;
  title: string;
  as: string;
}) => {
  return (
    <>
      {todoList.length !== 0 && (
        <>
          <TodoTitle title={title} as={as} />
          <ul>
            {todoList.map((todo) => (
              <li key={todo.id}>
                <TodoItem todo={todo} key={todo.id} toggleTodoListItemStatus={toggleTodoListItemStatus} deleteTodoListItem={deleteTodoListItem} />
              </li>
            ))}
          </ul>
        </>
      )}
    </>
  );
};

components/TodoTitle.tsx
import { memo } from "react";

// タイトルの表示コンポーネント
export const TodoTitle = memo(({ title, as }: { title: string; as: string}) => {
  if (as === "h1") {
    return <h1>{title}</h1>;
  } else if (as === "h2") {
    return <h2>{title}</h2>;
  } else {
    return <p>{title}</p>;
  }
});
components/TodoAdd.tsx
import { RefObject } from "react";

export const TodoAdd = ({ buttonText, inputEl, handleAddTodoListItem }: { buttonText: string; inputEl: RefObject<HTMLTextAreaElement>; handleAddTodoListItem: () => void }) => {
  return (
    <>
      <textarea ref={inputEl} />
      <button onClick={handleAddTodoListItem}>{buttonText}</button>
    </>
  );
};

api/todos.ts
import axios from "axios";
import { Todo } from "../types/Todo";

const todoDataUrl = "http://localhost:3100/todos";

// 全TODOリスト取得
export const getAllTodosData = async () => {
  const response = await axios.get(todoDataUrl);
  return response.data;
};

// 1件のTODOを追加する
export const addTodoData = async (todo: Todo) => {
  const response = await axios.post(todoDataUrl, todo);
  return response.data;
};

// 1件のTODOを削除する
export const deleteTodoData = async (id: string) => {
  await axios.delete(`${todoDataUrl}/${id}`);
  return id;
};

// 1件のTODOを更新する
export const updateTodoData = async (id: string, todo: Todo) => {
  const response = await axios.put(`${todoDataUrl}/${id}`, todo);
  return response.data;
};
hooks/useTodo.tsx
import { useState, useEffect } from "react";
import { ulid } from "ulid";

import * as todoData from "../api/todos";
import { Todo } from "../types/Todo";

export const useTodo = () => {
  const [todoList, setTodoList] = useState<Todo[]>([]);

  useEffect(() => {
    todoData.getAllTodosData().then((todo) => {
      console.log(...todo);
      setTodoList([...todo].reverse());
    });
  }, []);

  // todoのdoneを反転させる
  const toggleTodoListItemStatus = (id: string, done: boolean) => {
    // todoListから、idが一致する1件を取り出す
    const todoItem = todoList.find((item: Todo) => item.id === id);
    // doneを反転させて、新たなitemを作成
    const newTodoItem: Todo = { ...todoItem!, done: !done };
    // サーバに更新API呼ぶ
    todoData.updateTodoData(id, newTodoItem).then((updatedTodo) => {
      // 成功したら、todoListを更新。idが一致しているものを、サーバーから返ってきたupdatedTodoで更新する
      const newTodoList = todoList.map((item) => (item.id !== updatedTodo.id ? item : updatedTodo));
      // 新しいtodoListをstateにセットする
      setTodoList(newTodoList);
    });
  };

  const addTodoListItem = (todoContent: string) => {
    // あたらしいitemを作成する
    const newTodoItem = { id: ulid(), content: todoContent, done: false };

    // サーバーの追加APIを呼ぶ
    todoData.addTodoData(newTodoItem).then((addTodo) => {
      // addTodoをtodoListに追加してstateにセットする
      setTodoList([addTodo, ...todoList]);
    });
  };

  const deleteTodoListItem = (id: string) => {
    // サーバーの削除APIを呼ぶ
    todoData.deleteTodoData(id).then((deletedid) => {
      const newTodoList = todoList.filter((item) => item.id !== deletedid);
      // 1件削除された新しいtodoListに追加してstateにセットする
      setTodoList(newTodoList);
    });
  };

  // 作成した関数を返す
  return { todoList, toggleTodoListItemStatus, addTodoListItem, deleteTodoListItem };
};

types/Todo.ts
export type Todo = {
    id: string;
    content: string;
    done: boolean;
  };
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?