使用React实现可排序表的正确方法是什么?

时间:2017-04-15 14:28:53

标签: javascript reactjs

我目前的实施:

export const SORT_ORDER = {
    ASC: "ascending",
    DESC: "descending",
    OTHER: "other"
};

export default class Table extends React.Component {

    constructor(props) {
        super(props);

        this.sortIcon = new Map();
        this.sortIcon.set(SORT_ORDER.ASC, sortAsc);
        this.sortIcon.set(SORT_ORDER.DESC, sortDesc);
        this.sortIcon.set(SORT_ORDER.OTHER, sortOther);

        this.state = {
            sortField: this.props.defaultSortColumn,
            sortOrder: this.props.defaultSortOrder
        };
    }

    componentDidMount() {
        this.sort(this.props.defaultSortColumn, this.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;
        }
    };

    sort = (columnId, order) => () => {
        let descriptor = this.props.structure[columnId];
        let values = this.props.value.slice();

        let orderFactor = order === SORT_ORDER.ASC ? 1 : -1;
        values.sort((a, b) => {
            let first = descriptor.render(a);
            let second = descriptor.render(b);
            return first > second ? orderFactor : first < second ? -orderFactor : 0;
        });

        this.setState({
            sortField: columnId,
            sortOrder: order
        });
        this.props.onSort(values);
    };

    renderHeader = (id, descriptor) => {
        let order = this.retrieveOrder(id);
        let iconSrc = this.sortIcon.get(order);
        let nextOrder = this.nextOrder(this.retrieveOrder(id));
        return (
            <th key={id} className={descriptor.headStyle}>
                <a href="#" aria-sort={order} onClick={this.sort(id, nextOrder)}>
                    <img src={iconSrc}/>
                    {descriptor.label}
                </a>
            </th>
        );
    };

    render()  {
        return (
            <table>
                Table structure
            </table>
        );
    }
}

父组件以下一种方式声明它:

<Table structure={this.tableHeader} value={this.state.tableValue} onSort={this.handleChange('tableValue')}
     defaultSortColumn="created" defaultSortOrder={SORT_ORDER.DESC} />

表值在props中定义为valueonSort是一个更改父组件状态的函数=&gt;它会更改表值。此外,我还需要defaultSortColumndefaultSortOrder对表格进行排序后对其进行排序。

问题是我的表可以在页面上多次声明。

所以,

1)我无法将表值存储在其状态中。我应该吗?

2)如何在不使用componentDidMount的情况下实现默认排序?使用当前实现时,默认排序仅在调用componentDidMount时发生一次,但我在页面上有超过1个<Table/>组件。 我尝试使用componentWillReceiveProps函数,但在<Table/>函数中更改sort状态时也会调用它。所以我不能用它。

1 个答案:

答案 0 :(得分:0)

我的最终解决方案是:

root@beaglebone:/opt/scripts/boot# ifconfig
eth0      Link encap:Ethernet  HWaddr 04:a3:16:e1:5d:39
          UP BROADCAST MULTICAST DYNAMIC  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          Interrupt:173

eth1      Link encap:Ethernet  HWaddr 20:c9:d0:29:e0:88
          inet addr:169.254.84.230  Bcast:169.254.255.255  Mask:255.255.0.0
          inet6 addr: fe80::22c9:d0ff:fe29:e088/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST DYNAMIC  MTU:1500  Metric:1
          RX packets:56 errors:0 dropped:0 overruns:0 frame:0
          TX packets:136 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:6708 (6.5 KiB)  TX bytes:36844 (35.9 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:236 errors:0 dropped:0 overruns:0 frame:0
          TX packets:236 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:18852 (18.4 KiB)  TX bytes:18852 (18.4 KiB)

usb0      Link encap:Ethernet  HWaddr 04:a3:16:e1:5d:3b
          inet addr:192.168.7.2  Bcast:192.168.255.255  Mask:255.255.0.0
          inet6 addr: fe80::6a3:16ff:fee1:5d3b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:645 errors:0 dropped:0 overruns:0 frame:0
          TX packets:492 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:74285 (72.5 KiB)  TX bytes:98066 (95.7 KiB)

usb1      Link encap:Ethernet  HWaddr 04:a3:16:e1:5d:3e
          inet addr:192.168.6.2  Bcast:192.168.6.3  Mask:255.255.255.252
          inet6 addr: fe80::6a3:16ff:fee1:5d3e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:126 errors:0 dropped:0 overruns:0 frame:0
          TX packets:61 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:39046 (38.1 KiB)  TX bytes:13030 (12.7 KiB)

表用法示例:

export const SORT_ORDER = {
    ASC: "ascending",
    DESC: "descending",
    OTHER: "other"
};

class TableRow extends React.Component {

    render() {
        return (
            <tr>
                {this.props.children}
            </tr>
        );
    }
}


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 (
            <th>
               {onClick ? (
                <a href="#" aria-sort={sortOrder} onClick={onClick}>
                    <img src={this.sortIcon.get(sortOrder).icon} title={this.sortIcon.get(sortOrder).title} />
                    {children}
                </a>
            ) : children}
            </th>
        );
    }
}

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 = (
                    <TableHeader key={id} onClick={() => {this.setState({sortField: descriptor.attribute, sortOrder: nextOrder})}}
                        sortOrder={order}>
                        {descriptor.label}
                    </TableHeader>
                )
            } else {
                header = (
                    <TableHeader key={id}>
                        {descriptor.label}
                    </TableHeader>
                )
            }
            return header;
        });
    };

    renderRows = () => {
        const Row = this.props.customRow || TableRow;
        const values = this.props.sortable ? this.sortedRows() : this.props.value;
        return values.map((value, idx) => (
            <Row key={idx} value={value}>
                {this.props.structure.map((descriptor, id) => (
                    <td key={id}>
                        descriptor.render(value, idx)
                    </td>
                ))}
            </Row>
        ));
    };

    render()  {
        return (
            <table className={this.props.className}>
                <thead>
                    <tr>
                        {this.renderHeaders()}
                    </tr>
                </thead>
                <tbody>
                    {this.renderRows()}
                </tbody>
            </table>
        );
    }
}

实施基于http://styleguide.cfapps.io/react_base_tables.html