やりたいこと
- TypeScriptで共通コンポーネントを作成する際、抽象度を上げて汎用的に使いたい
- ジェネリクスを使って汎用性を上げる
想定ケース
- テーブルコンポーネントを作成して、共通部品として使用する
- 共通コンポーネントへは、「レコードデータ / カラムデータ / ヘッダー情報」 を props で渡す
- 今回はヘッダー情報のみ、フロントで保持する
コード
型情報
types/table.ts
type Props<T> = Partial<T> & {
width?: string | number;
};
// ヘッダーのlabelと幅を定義、さらにデザイン済みのReactNodeを含める
type ColumnObject<T> = {
headerLabel: string;
headerWidth?: string | number;
// itemの幅はヘッダーの幅と同一とするため、headerWidthの値を使用
item: (_props: Props<T>) => React.ReactNode;
};
// fetchしたデータ以外に「編集ボタン」など、UIにしかないカラムを定義
type ColumnsKey<T> = keyof T | 'editButton';
// 表示させるカラムkeyと、そのkeyに連動した中身を定義
export type Columns<T> = {
[_key in ColumnsKey<T>]?: ColumnObject<T>;
};
共通コンポーネント側
CommonTable.tsx「通常のfunctionで記述したケース」
// 共通コンポーネントなので、型定義は全てジェネリクス
interface Props<T> {
columns: Columns<T>;
records: T[];
}
// function Component<T>(props: Props<T>) {} の記述にすることで、共通コンポーネントを使用する側で、型を指定できる
export function CommonTable<T>({ columns, records }: Props<T>) {
// 省略
return (
<>
<div className={styles.head}>{headers}</div>
{records.map((record) => (
<div className={styles.record}>{record}</div>
))}
</>
);
}
CommonTable.tsx「アロー関数で記述したケース」
// 共通コンポーネントなので、型定義は全てジェネリクス
interface Props<T> {
columns: Columns<T>;
records: T[];
}
// 使用側で型を指定
export const CommonTable = <T,>({ columns, records }: Props<T>) => {
// 省略
return (
<>
<div className={styles.head}>{headers}</div>
{records.map((record) => (
<div className={styles.record}>{record}</div>
))}
</>
);
}
関数のジェネリクスについては、筆者が以前に以下の記事を書いたので、もしご興味がございましたらご覧ください。
使用側
pages/list.ts
export const UserList = () => {
// 省略
const columns = getItems();
return (
{/* 共通コンポーネント使用時に、型を指定 */}
<CommonTable<FetchTableData>
records={data}
columns={columns}
/>
)
}
items.tsx
// デザイン済みのitem
export const getItems = (): Columns<FetchTableData> => {
// 省略
return {
title: {
headerLabel: 'title',
headerWidth: '50%',
item: ({ title, width }) => (
<Text flexBasis={width}>
{title}
</Text>
),
},
createdAt: {
headerLabel: 'created_at',
headerWidth: '20%',
children: ({ createdAt, width }) => (
<Text flexBasis={width}>
{getFormattedDate(createdAt, 'yyyy年MM月dd日')}
</Text>
),
},
};
};
まとめ
- 共通コンポーネント側
- Functional Component定義時に、関数ジェネリクスで記述する
- 例:
function Component<T>(props: Props<T>) {}
- アロー関数での記述例:
const Component = <T,>(props: Props<T>) {}
- 使用側
- 共通コンポーネント使用時に、型を指定
- 例:
<Common<FetchData> />