我的最终解决方案是:
export const SORT_ORDER = {
ASC: "ascending",
DESC: "descending",
OTHER: "other"
};
class TableRow extends React.Component {
render() {
return (
{this.props.children}
);
}
}
class TableHeader extends React.Component {
constructor(props) {
super(props);
this.sortIcon = new Map([
[SORT_ORDER.ASC, {icon: sortAsc, title: "Ascending"}],
[SORT_ORDER.DESC, {icon: sortDesc, title: "Descending"}],
[SORT_ORDER.OTHER, {icon: sortOther, title: "Unsorted"}]
]);
}
render() {
const {children, onClick, sortOrder} = this.props;
return (
{onClick ? (
{children}
) : children}
);
}
}
export default class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
sortField: props.defaultSortColumn,
sortOrder: props.defaultSortOrder
};
}
retrieveOrder = (columnId) => {
return columnId === this.state.sortField ? this.state.sortOrder : SORT_ORDER.OTHER;
};
nextOrder = (current) => {
if (current === SORT_ORDER.DESC) {
return SORT_ORDER.ASC;
} else if (current === SORT_ORDER.ASC) {
return SORT_ORDER.DESC;
} else {
return this.props.defaultSortOrder;
}
};
sortedRows = () => {
let descriptor = this.props.structure.find(d => d.attribute === this.state.sortField);
let values = this.props.value.slice();
let orderFactor = this.state.sortOrder === SORT_ORDER.ASC ? 1 : -1;
return values.sort((a, b) => {
let first;
let second;
// null and undefined values should be changed to empty string
if (typeof a[descriptor.attribute] === "number" || typeof b[descriptor.attribute] === "number") {
first = a[descriptor.attribute] || "";
second = b[descriptor.attribute] || "";
} else {
first = descriptor.render(a) || "";
second = descriptor.render(b) || "";
}
return first > second ? orderFactor : first < second ? -orderFactor : 0;
});
};
renderHeaders = () => {
return this.props.structure.map((descriptor, id) => {
let header;
if (this.props.sortable) {
const order = this.retrieveOrder(descriptor.attribute);
const nextOrder = this.nextOrder(order);
header = (
{this.setState({sortField: descriptor.attribute, sortOrder: nextOrder})}}
sortOrder={order}>
{descriptor.label}
)
} else {
header = (
{descriptor.label}
)
}
return header;
});
};
renderRows = () => {
const Row = this.props.customRow || TableRow;
const values = this.props.sortable ? this.sortedRows() : this.props.value;
return values.map((value, idx) => (
{this.props.structure.map((descriptor, id) => (
descriptor.render(value, idx)
))}
));
};
render() {
return (
{this.renderHeaders()}
{this.renderRows()}
);
}
}
表用法示例:
this.tableStructure = [
{
attribute: "number", label: "Row Number"
render: (row) => {return row.number}
},
{
attribute: "created", label: "Creation time"
render: (row) => {return this.dateToString(row.created)}
},
{
attribute: "type", label: "Row Type"
render: (row) => {return row.type}
},
{
attribute: "state", label: "State",
render: (row) => {return row.state}
},
{
attribute: "action", label: "Action"
render: (row) => {
return (
);
}
}
];
defaultSortColumn="created" defaultSortOrder={SORT_ORDER.DESC} />