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
コードをコピペ。
[ 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;
-
まぁいいか
📎 アロー関数と通常関数
アロー関数は多くの場面で便利ですが、特定の状況では従来の関数宣言を使用する方が適切な場合があります。アロー関数を使用しない理由として考えられる主な点は以下の通りです:
アロー関数は自身の
this
を持たず、外部のスコープからthis
を継承します。この特性は多くの場面で便利ですが、オブジェクトのメソッドやプロトタイプメソッド、イベントハンドラとして関数を定義する場合には問題を引き起こすことがあります。従来の関数宣言では、関数が呼ばれたコンテキストに基づいてthis
の値が設定されるため、オブジェクト指向のパターンで期待される動作を実現しやすくなります。アロー関数はコンストラクタとして使用することができません。つまり、
new
キーワードを使用してアロー関数のインスタンスを作成することはできません。クラス構文が普及していますが、従来の関数によるコンストラクタパターンを使用するライブラリやフレームワークでは、従来の関数宣言が依然として重要です。アロー関数内では
arguments
オブジェクトが利用できません。これは関数内で利用できるローカル変数で、関数に渡されたすべての引数を含んでいます。可変長の引数を扱う場合、従来の関数宣言ではarguments
オブジェクトを使用することが一般的です。アロー関数ではスプレッド構文や残余パラメータを使用することで似たような機能を実現できますが、場合によっては従来の方法が便利なこともあります。アロー関数は匿名関数です。そのため、デバッグ時に関数名が表示されないことがあり、トレースが難しくなることがあります。従来の関数宣言を使用すれば、関数に名前を付けることができ、デバッグが容易になります。
特定のプロジェクトやチームによっては、アロー関数の使用がコードの可読性を低下させると判断されることがあります。特に、複数行にわたる処理を含む関数の場合、
function
キーワードを使用した方が伝統的な関数の構造が明確で読みやすくなる場合があります。また、特定のコーディングスタイルや慣習が理由で従来の関数宣言が推奨されることもあります。これらの理由から、アロー関数と従来の関数宣言のどちらを使用するかは、具体的な使用状況や目的に応じて選択することが重要です。
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型を定義する
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";
// 省略
( 見た目には変化なし )
カテゴリーを動的に表示する
⇒ 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>
);
}
同様に、アイテムの動的表示をする。
ポイント
- key を設定するので、そこで1つ を使う
-
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)