【TypeScript】フロントエンド開発におけるReactを利用したToDoアプリ開発

本記事では、以下の構成フォルダにてTypeScriptによるToDoアプリ開発を解説します。

todo-app
├─public
│ ├─index.html
│ └─(Create React Appの場合にその他ファイル)
├─src
│ ├─componets
│ │ └─App.tsx
│ └─index.tsx
└─package.json等
目次

TypeScriptとJavaScriptの違い

TypeScriptは、”JavaScript + 静的型付け”である言語です。

つまり、端的にTypeScriptとJavaScriptの違いで言えば、静的型付けが利用できるかどうかになります。

TypeScriptとJavaScriptがどのような関係にあるか理解することで、TypeScriptの学習の上で重要になります。

そのため、JavaScriptよりも覚えることが多く、比較すると難易度が高めになります。

TypeScript等の開発環境構築

TypeScriptによるアプリ開発を実施するためには、プログラミング言語に限らず開発環境構築が必要です。

TypeScriptを利用するためには、以下の開発環境を整える必要があります。

TypeScriptの開発環境構築
  • Node.jsのインストール
  • TypeScriptのインストール
  • VSCodeのインストール

TypeScriptを利用するために、Node.jsのインストールが必要になります。

基本的に、最新版をインストールすれば問題ありません。

各種必要なインストーラーの実施方法を詳しく知りたい人は、「【TypeScript】インストール方法や開発環境構築を徹底解説!」を一読ください。

TypeScriptとReactにおけるプロジェクト作成

必要なプログラミング言語・ソフトウェアをインストールできた段階で、アプリ開発時のプロジェクトを作成します。

cd [任意のディレクトリ名]
mkdir [任意のディレクトリ作成]
cd [作成したディレクトに移動]

あなたが利用したい任意のディレクトリまで移動し、プロジェクトフォルダを作成してください。

作成後、作成したディレクトリに移動します。

TypeScriptとReactを利用したToDoアプリ開発

あくまでフロントエンド開発の一つのチュートリアルとして参考ください。(サーバーサイドは触れない)

ここでは、TypeScriptとReactフレームワークを利用して簡易的なToDoアプリを開発していきます。

npx create-react-app [任意のプロジェクト名] --template typescript

上記のコマンドを任意のディレクトリで実行すると、TypeScriptとReactの新規プロジェクトが作成されます。

また、初期配置のプロジェクトファイルは主に以下の構成で進めていきます。

todo-app
├─public
│ ├─index.html
│ └─(Create React Appの場合にその他ファイル)
├─src
│ ├─componets
│ │ └─App.tsx
│ └─index.tsx
└─package.json等

上記を踏まえた上で、各ファイルのコーディングについて解説します。

また、事前準備として、index.tsxに以下を記述します。

import { render } from "react-dom";
import { App } from "./components/App";

const rootElement = document.getElementById("root");
render(<App />, rootElement);

モジュール間でデータを受け渡す方法としてimport宣言とexport宣言がありますが、ここではimport宣言によって各モジュールからデータを受け取っています。

また、変数rootElementを定義し、Appオブジェクトをレンダリングしています。

App.tsxにおける実装

ここでは、まず全ての処理をApp.tsxに記述している例になります。

import { ChangeEvent, useState, FC } from "react";
import styled from "styled-components";

export const App: FC = () => {
  //テキストボックスStateの記述
  const [text, setText] = useState<string>("");
  //メモ一覧Stateの記述
  const [memos, setMemos] = useState<string[]>([]);

  //テキストボックス入力時に入力内容をStateに設定
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => setText(e.target.value);

  //[追加]ボタン押下時
  const onClickAdd = () => {
    //State変更を正常に検知させるために新たな配列を生成
    const newMemos = [...memos];
    //テキストボックスの入力内容をメモ配列に追加
    newMemos.push(text);
    setMemos(newMemos);
    //テキストボックスを空にする
    setText("");
  };

  //[削除]ボタン押下時(何番目が押されたか引数で受け取る)
  const onClickDelete = (index: number) => {
    //State変更を正常に検知させるため新たな配列を生成
    const newMemos = [...memos];
    //メモ配列から該当の要素を削除
    newMemos.splice(index, 1);
    setMemos(newMemos);
  };

  return (
    <div>
      <h1>ToDo App</h1>
      <input type="text" value={text} onChange={onChangeText} />
      <SButton onClick={onClickAdd}>Add</SButton>
      <SContainer>
        <p>Memos</p>
        <ul>
          {memos.map((memo, index) => (
            <li key={memo}>
              <SMemoWrapper>
                <p>{memo}</p>
                <SButton onClick={() => onClickDelete(index)}>Delete</SButton>
              </SMemoWrapper>
            </li>
          ))}
        </ul>
      </SContainer>
    </div>
  );
};

const SButton = styled.button`
  margin-left: 16px;
`;

const SContainer = styled.div`
  border: solid 1px #ccc;
  padding: 16px;
  margin: 8px;
`;

const SMemoWrapper = styled.div`
  display: flex;
  align-items: center;
`;

import文では、各種必要なモジュールからデータを受け取っています。

また、CSSライブラリは何を利用してもよいですが、本プログラムではstyled-componentsを利用しています。

export文ではAppに関する処理を実装しています。

最後に、ボタン等の定義を実装しています。

コンポーネント化における実装

フロントエンド開発では、コンポーネント化といった概念も理解しておく必要があります。

ここでは、メモ一覧を表示させるエリアをコンポーネント化できるので、例として以下のコードを作成します。

その際のフォルダ構成を一部追加します。

todo-app
├─public
│ ├─index.html
│ └─(Create React Appの場合にその他ファイル)
├─src
│ ├─componets
│ │ ├─App.tsx
│ │ └─MemoList.tsx(ファイル追加)
│ └─index.tsx
└─package.json等

作成したMemoList.tsxに対してApp.tsxの一部画面要素を移植していきます。

import { FC } from "react";
import styled from "styled-components";

//必要なPropsはメモ一覧と削除時に実行する関数
type Props = {
    memos: string[];
    onClickDelete: (index: number) => void;
};

export const MemoList: FC<Props> = props => {
    const { memos, onClickDelete } = props;

    return (
        <SContainer>
        <p>Memos</p>
        <ul>
          {memos.map((memo, index) => (
            <li key={memo}>
              <SMemoWrapper>
                <p>{memo}</p>
                <SButton onClick={() => onClickDelete(index)}>Delete</SButton>
              </SMemoWrapper>
            </li>
          ))}
        </ul>
      </SContainer>
    );
};

const SButton = styled.button`
  margin-left: 16px;
`;

const SContainer = styled.div`
  border: solid 1px #ccc;
  padding: 16px;
  margin: 8px;
`;

const SMemoWrapper = styled.div`
  display: flex;
  align-items: center;
`;

MemoList.tsxにて、メモ一覧部分をコンポーネント化に成功しました。

また、一部画面要素を移植できたので、App.tsxの修正が必要になります。

import { ChangeEvent, useState, FC, useCallback } from "react";
import styled from "styled-components";
import { MemoList } from "./MemoList";

export const App: FC = () => {
  //テキストボックスStateの記述
  const [text, setText] = useState<string>("");
  //メモ一覧Stateの記述
  const [memos, setMemos] = useState<string[]>([]);

  //テキストボックス入力時に入力内容をStateに設定
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => setText(e.target.value);

  //[追加]ボタン押下時
  const onClickAdd = () => {
    //State変更を正常に検知させるために新たな配列を生成
    const newMemos = [...memos];
    //テキストボックスの入力内容をメモ配列に追加
    newMemos.push(text);
    setMemos(newMemos);
    //テキストボックスを空にする
    setText("");
  };

  //[削除]ボタン押下時(何番目が押されたか引数で受け取る)
  const onClickDelete = useCallback((index: number) => {
    //State変更を正常に検知させるため新たな配列を生成
    const newMemos = [...memos];
    //メモ配列から該当の要素を削除
    newMemos.splice(index, 1);
    setMemos(newMemos);
  }, [memos]);

  return (
    <div>
      <h1>ToDo App</h1>
      <input type="text" value={text} onChange={onChangeText} />
      <SButton onClick={onClickAdd}>Add</SButton>
      <MemoList memos={memos} onClickDelete={onClickDelete} />
    </div>
  );
};

const SButton = styled.button`
  margin-left: 16px;
`;

ファイル変更前と比較して、コンポーネント化した部分をApp.tsxから追加・削除したことが分かります。

このようにコンポーネント化によって得られるメリットがいくつか存在します。

コンポーネント化によるメリット
  • 画面の各要素をコンポーネント化し使いまわせる
  • コンポーネント一つを修正することで全体に変更が適用される

カスタムフック化における実装

最後に、ToDoに関するロジックとメモ一覧データをカスタムフックによって分離してみましょう。

hooksフォルダを作成し、配下にuseMemoList.tsをカスタムフックとして作成し実装します。

todo-app
├─public
│ ├─index.html
│ └─(Create React Appの場合にその他ファイル)
├─src
│ ├─componets
│ │ ├─App.tsx
│ │ └─MemoList.tsx
│ ├─hooks(フォルダ追加)
│ │ └─useMemoList.ts(ファイル追加)
│ └─index.tsx
└─package.json等

hooksフォルダとuseMemoList.tsを追加しています。

先に、useMemoList.tsから実装していきます。

import { useCallback, useState } from "react";

//メモ一覧に関するカスタムフック
export const useMemoList = () => {
    //メモ一覧State
    const [memos, setMemos] = useState<string[]>([]);
    
    //メモ追加ロジック
    const addTodo = useCallback((text: string) => {
        //State変更を正常に検知させるために新たな配列を生成
        const newMemos = [...memos];
        //テキストボックスの入力内容をメモ配列に追加
        newMemos.push(text);
        setMemos(newMemos);
        //依存配列に忘れずにmemosを設定
    }, [memos]);

    //メモ削除ロジック
    const deleteTodo = useCallback((index: number) => {
        //State変更を正常に検知させるため新たな配列を生成
        const newMemos = [...memos];
        //メモ配列から該当の要素を削除
        newMemos.splice(index, 1);
        setMemos(newMemos);
    }, [memos]);

    return { memos, addTodo, deleteTodo };
};

作成した各関数(addTodoとdeleteTodo)がそれぞれ追加ロジックと削除ロジックを担います。

このようにロジックを分離することで他のコンポーネントでも使いまわすことができます。

また、一部機能を移植できたので、App.tsxの修正が必要になります。

import { ChangeEvent, useState, FC, useCallback } from "react";
import styled from "styled-components";
import { MemoList } from "./MemoList";
import { useMemoList } from "../hooks/useMemoList";

export const App: FC = () => {
  //カスタムフックからそれぞれ取得
  const { memos, addTodo, deleteTodo } = useMemoList();
  //テキストボックスStateの記述
  const [text, setText] = useState<string>("");

  //テキストボックス入力時に入力内容をStateに設定
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => setText(e.target.value);

  //[追加]ボタン押下時
  const onClickAdd = () => {
    //カスタムフックのメモ追加ロジック実行
    addTodo(text);
    //テキストボックスを空にする
    setText("");
  };

  //[削除]ボタン押下時(何番目が押されたか引数で受け取る)
  const onClickDelete = useCallback((index: number) => {
    //カスタムフックのメモ削除ロジック実行
    deleteTodo(index); 
  }, [deleteTodo]);

  return (
    <div>
      <h1>ToDo App</h1>
      <input type="text" value={text} onChange={onChangeText} />
      <SButton onClick={onClickAdd}>Add</SButton>
      <MemoList memos={memos} onClickDelete={onClickDelete} />
    </div>
  );
};

const SButton = styled.button`
  margin-left: 16px;
`;

ファイル変更前に比べると、カスタムフック化した部分をApp.tsxから追加・削除したことが分かります。

このようにカスタムフック化によって得られるメリットがいくつか存在します。

カスタムフック化によるメリット
  • ロジックをコンポーネントから分離できる
  • 複数コンポーネントにロジックを再利用できる

コンポーネント化とカスタムフック化を理解することで、フロントエンドにおけるアプリ開発時の効率アップにつながります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次