it's been 2 days i'm struggling to make a reusable table but i have problems with types.
I'm not a typescript/react expert because i worked more with javascript so i would like you to show me the right way to develop this component.
Info:
typescript: "3.6.4"
react: "^16.11.0"
"engines": {
"node": "10.x",
"npm": "6.x"
}
My idea is (uncomplete and with types errors):
import * as React from 'react';
const getCellData = (fields: Record<string, string>, row) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return Object.entries(fields).reduce((res: Record<string, any>, val) => {
res[val[0]] = row[val[1]];
return res;
}, {});
};
interface Props<C, R> {
columns: C[];
rows: R[];
}
const Table = <C, R>({ columns, rows }: Props<C, R>) => {
return (
<div className="table-responsive">
<table className="table table-striped">
<thead>
<tr>
{columns.map((column, i: number) => (
<th scope="col" key={i}>
{column.label}
</th>
))}
</tr>
</thead>
<tbody>
{rows &&
rows.map((row, j: number) => {
return (
<tr key={j}>
{columns.map((column, k: number) => {
const { Template, fields } = column;
const data = getCellData(fields, row);
return <td key={k}>{<Template {...data}></Template>}</td>;
})}
</tr>
);
})}
</tbody>
</table>
</div>
);
};
export default Table;
I'm using the table like this in JSX (Is it right to pass types in jsx like this?):
<Table<SportsTableColumn, RedaxSport> columns={columns} rows={sports}></Table>
where SportsTableColumn is:
export interface SportsTableColumn {
label: string;
Template: typeof TableTdText | typeof TableTdTextWithImage | typeof TableTdBoolCheck;
fields: {
text?: string;
image?: string;
checked?: string;
};
}
TableTdText, TableTdTextWithImage, TableTdBoolCheck are React.FC
Then we have the RedaxSport that is a class with properties like:
this.sportId = sportId;
this.urlIcon = urlIcon;
this.redaxDescription = redaxDescription;
this.aamsDescription = aamsDescription;
this.isDailyProgram = isDailyProgram;
"columns" array data example:
[
{
label: intl.formatMessage({ id: 'sports.table.redaxDescription' }),
Template: TableTdTextWithImage,
fields: {
text: 'redaxDescription',
image: 'urlIcon'
}
},
{
label: intl.formatMessage({ id: 'sports.table.aamsDescription' }),
Template: TableTdText,
fields: {
text: 'aamsDescription'
}
},
{
label: intl.formatMessage({ id: 'sports.table.dailyProgram' }),
Template: TableTdBoolCheck,
fields: {
checked: 'isDailyProgram'
}
}
];
"rows" data example:
[
{
"sportId": 0,
"urlIcon": "https://url.com/",
"redaxDescription": "This is a description",
"aamsDescription": "Another description",
"isDailyProgram": true
},
{
"sportId": 1,
"urlIcon": "https://url2.com/",
"redaxDescription": "This is a description 2",
"aamsDescription": "Another description 2",
"isDailyProgram": false
}
]
I just hope to have been clear about my intent and gave you all the info you need. Thank you
Have a look at a generic table for example
It can be reused and does not requires a lot of code
Example of using the generic table:
type MyElement = {
firstName: string;
lastName: string;
age: number;
};
const elements: MyElement[] = [
{ firstName: 'A', lastName: 'A', age: 10 },
{ firstName: 'B', lastName: 'B', age: 11 },
{ firstName: 'C', lastName: 'C', age: 12 },
{ firstName: 'D', lastName: 'D', age: 13 },
{ firstName: 'E', lastName: 'E', age: 14 },
];
const model: TableModel<MyElement> = {
columns: [
{
title: 'Name',
html: e => <span>{e.firstName} {e.lastName}</span>
},
{
title: 'Firstname',
html: e => <span>{e.firstName}</span>
},
{
title: 'Lastname',
html: e => <span>{e.lastName}</span>
},
{
title: 'Age',
html: e => <span>{e.age}</span>
},
]
};
export const Table = () => {
return (
<div className={styles.tableContainer}>
<GenericTable model={model} elements={elements}></GenericTable>
</div>
);
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With