RTQ_React04 - wahei628/ShareSuke GitHub Wiki

https://www.notion.so/04_ToDo-30d903a6021b47869a2e25353d8a0389

ブランチ

04_todo_index

📎 目次

学習の意識ポイント

  • コンポーネントの構造を知る
  • React HooksのuseState機能
  • TypeScriptの型定義

⭐ ディレクトリ構造

app 
 -- javascript 
				----- react                         
								 ----- entrypoints  
								            ----- todo_app.tsx
								 ----- features
														 |
													 todos	 
														 ----- index.tsx

実践フェーズ

1. Todosコンポーネントの基本構造の設定

コードをコピペ。

[ app/javascript/react/features/todos/index.tsx ]

[ 前半 ] …これは、…初期値を直入れしたのかな……..?

import React from "react";

// 初期アイテムモックリストの定義
const initialItems = [
  { id: 1, title: 'アイテム1', content: 'アイテム1', category: 'NoStatus', assignee: '未割り当て', position: 10, category_id: 1},
  { id: 2, title: 'アイテム2', content: 'アイテム2', category: 'NoStatus', assignee: '未割り当て', position: 20, category_id: 1 },
  { id: 3, title: 'アイテム3', content: 'アイテム3', category: 'NoStatus', assignee: 'らんてくん', position: 30, category_id: 1 },
  { id: 4, title: 'アイテム4', content: 'アイテム4', category: 'NoStatus', assignee: '未割り当て', position: 40, category_id: 1 },
  { id: 5, title: 'アイテム5', content: 'アイテム5', category: 'NoStatus', assignee: 'らんくん', position: 50, category_id: 1 },
  { id: 6, title: 'アイテム6', content: 'アイテム6', category: 'NoStatus', assignee: '未割り当て', position: 60, category_id: 1 },
  { id: 7, title: 'アイテム7', content: 'アイテム7', category: 'NoStatus', assignee: '未割り当て', position: 70, category_id: 1 },
  { id: 8, title: 'アイテム8', content: 'アイテム8', category: 'NoStatus', assignee: '未割り当て', position: 80, category_id: 1 },
  { id: 9, title: 'アイテム9', content: 'アイテム9', category: 'InProgress', assignee: '未割り当て', position: 10, category_id: 3 },
  { id: 10, title: 'アイテム10', content: 'アイテム10', category: 'Done', assignee: '未割り当て', position: 10, category_id: 4 }
];
// 初期カテゴリーモックリストの定義
const initialCategories = [
  { id: 1, name: 'NoStatus'},
  { id: 2, name: 'Backlog'},
  { id: 3, name: 'InProgress'},
  { id: 4, name: 'Done'}
];

[ 後半 ] …ここから、render() の中身をコンポーネント化するのだろう…。

export default function Todos() {
  return (
    <div className="my-4 flex max-h-[90%] overflow-x-auto">
        <div
          className="ml-4 max-h-[100%] flex-none self-start overflow-y-auto rounded border border-gray-200 bg-base-300"
        >
          <div className="sticky top-0 z-10 flex rounded bg-base-300 p-2">
            <div className="flex-1">カテゴリータイトル</div>
          </div>
          <div
            className="min-h-[100px] w-[350px]"
          >
              <div>
                <div
                  className="m-2 rounded border border-gray-200 bg-base-100 hover:bg-base-200"
                >
                  <div className="relative flex min-h-[80px] flex-col">
                    <div className="link m-2 mr-10 break-words text-sm">アイテムタイトル</div>
                  </div>
                </div>
              </div>
            </div>
        </div>
    </div>
  );
}

ここでだけきになるんだよなぁ

export default function Todos() {};

アロー関数じゃだめなんか ??

const Todos = () => {};

export default Todos;
  • まぁいいか

    📎 アロー関数と通常関数

    アロー関数は多くの場面で便利ですが、特定の状況では従来の関数宣言を使用する方が適切な場合があります。アロー関数を使用しない理由として考えられる主な点は以下の通りです:

    1. thisの挙動

    アロー関数は自身のthisを持たず、外部のスコープからthisを継承します。この特性は多くの場面で便利ですが、オブジェクトのメソッドやプロトタイプメソッド、イベントハンドラとして関数を定義する場合には問題を引き起こすことがあります。従来の関数宣言では、関数が呼ばれたコンテキストに基づいてthisの値が設定されるため、オブジェクト指向のパターンで期待される動作を実現しやすくなります。

    2. コンストラクタ関数

    アロー関数はコンストラクタとして使用することができません。つまり、newキーワードを使用してアロー関数のインスタンスを作成することはできません。クラス構文が普及していますが、従来の関数によるコンストラクタパターンを使用するライブラリやフレームワークでは、従来の関数宣言が依然として重要です。

    3. 引数オブジェクト

    アロー関数内ではargumentsオブジェクトが利用できません。これは関数内で利用できるローカル変数で、関数に渡されたすべての引数を含んでいます。可変長の引数を扱う場合、従来の関数宣言ではargumentsオブジェクトを使用することが一般的です。アロー関数ではスプレッド構文や残余パラメータを使用することで似たような機能を実現できますが、場合によっては従来の方法が便利なこともあります。

    4. 明確な関数名

    アロー関数は匿名関数です。そのため、デバッグ時に関数名が表示されないことがあり、トレースが難しくなることがあります。従来の関数宣言を使用すれば、関数に名前を付けることができ、デバッグが容易になります。

    5. 可読性と慣習

    特定のプロジェクトやチームによっては、アロー関数の使用がコードの可読性を低下させると判断されることがあります。特に、複数行にわたる処理を含む関数の場合、functionキーワードを使用した方が伝統的な関数の構造が明確で読みやすくなる場合があります。また、特定のコーディングスタイルや慣習が理由で従来の関数宣言が推奨されることもあります。

    これらの理由から、アロー関数と従来の関数宣言のどちらを使用するかは、具体的な使用状況や目的に応じて選択することが重要です。



2. useState の追加

import React, { useState } from "react"; // 追加

// 省略

export default function Todos() {
  const [items, setItems] = useState<Item[]>(initialItems); // 追加
  const [categories, setCategories] = useState<Category[]>(initialCategories); // 追加

// 省略

なんでアロー関数つかってないん ????

export default 1 行 で書けるから ??

この <>、何なん ??

useState<Item[]>(initialItems)

⇒ TypeScriptの機能 、型を意識したやーつ。

useStateフックの内部で保持される状態の型

⇒ この構文は「ジェネリック型」と呼ばれる。

特定の関数やクラスが様々な型で動作するように設計する際に用いられる。

( 例 )

ジェネリック型 ( 動的な型 ) ( 柔軟な型 )

interface Container<T> {
  value: T;  // 使用する際に具体的な型で置き換えられる
}

let stringContainer: Container<string> = { value: "Hello, world!" };
let numberContainer: Container<number> = { value: 42 };

これも同様で、<Item[]> とあるので、

Item型 を定義する必要がある。

今回、コピペした実数値のオブジェクトのキーを見てみよう

{ id: 1, title: 'アイテム1', content: 'アイテム1', category: 'NoStatus', assignee: '未割り当て', position: 10, category_id: 1}

これをもとに、interfaceを使用してItem型を定義する


3. 型定義の設定

Item 型

export interface Item {
  id: number;
  title: string;
  content: string;
  category: string;
  assignee: string;
  position: number;
  category_id: number;
}

同様に、Category 型

export interface Category {
  id: number;
  name: string;
}

上記のようになる。

これを記述するファイルを別で作成し、そこからインポートする形をとる。

app 
 -- javascript 
				----- react                         
								 ----- entrypoints  
								            ----- todo_app.tsx
								 ----- features
														 |
													 todos	 
														 ------ index.tsx
													   ------ types ---- index.ts

[ app/javascript/react/features/todos/types/index.ts ]

export interface Item {
  id: number;
  title: string;
  content: string;
  category: string;
  assignee: string;
  position: number;
  category_id: number;
}

export interface Category {
  id: number;
  name: string;
}

インポートする文を書く

[ app/javascript/react/features/todos/types/index.ts ]

import React, { useState } from "react";
import { Item, Category } from "./types";

// 省略

Untitled

( 見た目には変化なし )


4. カテゴリーの動的表示

カテゴリーを動的に表示する

⇒ categories配列を使用してカテゴリーごとのコンテナを作成する

[ app/javascript/react/features/todos/index.tsx ]

// 省略
return (
    <div className="my-4 flex max-h-[90%] overflow-x-auto">
      {categories.map((category) => (
        <div
           key={category.id}
          className="ml-4 max-h-[100%] flex-none self-start overflow-y-auto rounded border border-gray-200 bg-base-300"
        >
          <div className="sticky top-0 z-10 flex rounded bg-base-300 p-2">
            <div className="flex-1">{category.name}</div>
          </div>
          <div
            className="min-h-[100px] w-[350px]"
          >
              <div>
                <div
                  className="m-2 rounded border border-gray-200 bg-base-100 hover:bg-base-200"
                >
                  <div className="relative flex min-h-[80px] flex-col">
                    <div className="link m-2 mr-10 break-words text-sm">アイテムタイトル</div>
                  </div>
                </div>
              </div>
            </div>
        </div>
      ))}
    </div>
  );

うーん、 className が邪魔で見づらすぎるので一回削除

// 省略
return (
    <div>
      {categories.map((category) => (
        <div key={category.id}>
          <div>
            <div>{category.name}</div>
          </div>
          <div>
              <div>
                <div>
                  <div>
                    <div>アイテムタイトル</div>
                  </div>
                </div>
              </div>
            </div>
        </div>
      ))}
    </div>
  );

これを仮想DOM で表示するため、 mapメソッドを使用して動的表示をしている。

categories というのは、userState にあるやつ。

( const [categories, setCategories] 名前、なんていうのかは忘れた )

因みに、 before はコレ

  return (
    <div>
        <div>
          <div>
            <div>カテゴリータイトル</div>
          </div>
          <div>
              <div>
                <div>
                  <div>
                    <div>アイテムタイトル</div>
                  </div>
                </div>
              </div>
            </div>
        </div>
    </div>
  );
}

アイテムの動的表示

同様に、アイテムの動的表示をする。

ポイント

  1. key を設定するので、そこで1つ
    を使う
  2. CSS のせいで、この上なく最上級に見づらい。

    これを、そのままカリキュラムに載せるのはどうかね…。

    // 省略
    
    export default function Todos() {
      const [items, setItems] = useState<Item[]>(initialItems);
      const [categories, setCategories] = useState<Category[]>(initialCategories);
    
      const filteredItems = (categoryId: number) => {
        return items
          .filter(
            (item) =>
              item.category_id === categoryId
          ).sort((a, b) => a.position - b.position)
      }
    
      return (
        <div className="my-4 flex max-h-[90%] overflow-x-auto">
          {categories.map((category) => (
            <div
              key={category.id}
              className="ml-4 max-h-[100%] flex-none self-start overflow-y-auto rounded border border-gray-200 bg-base-300"
            >
              <div className="sticky top-0 z-10 flex rounded bg-base-300 p-2">
                <div className="flex-1">{category.name}</div>
              </div>
              <div
                className="min-h-[100px] w-[350px]"
              >
                {filteredItems(category.id).map((item) => (
                  <div key={item.id}>
                    <div
                      className="m-2 rounded border border-gray-200 bg-base-100 hover:bg-base-200"
                    >
                      <div className="relative flex min-h-[80px] flex-col">
                        <div className="link m-2 mr-10 break-words text-sm">{item.title}</div>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      );
    }

    CSS スタイリング を削除

    const filteredItems = (categoryId: number) => {
        return items
          .filter((item) => item.category_id === categoryId)
          .sort((a, b) => a.position - b.position);
    }
    
    return (
        <div>
          {categories.map((category) => (
            <div key={category.id}>
              <div>
                <div>{category.name}</div>
              </div>
              <div>
                {filteredItems(category.id).map((item) => (
                  <div key={item.id}>
                    <div>
                      <div>
                        <div>{item.title}</div>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      );

    この辺はコールバックやでぇぇぇ

    items.filter((item) => item.category_id === categoryId)
    .sort((a, b) => a.position - b.position)
⚠️ **GitHub.com Fallback** ⚠️