2017-04-15 77 views
3

我目前的执行情况:使用React实现可排序表的正确方法是什么?

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} /> 

表值在道具定义为valueonSort是一个函数,用于更改父组件的状态=>它更改表值。另外,我还有defaultSortColumndefaultSortOrder可以在填充表格后排序表格。

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

所以,

1)我无法将表格值存储在其状态。我是不是该?

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

+0

我会建议使用生产 –

+0

@MartinZinovsky现有的解决方案现有的解决方案不符合要求,因为有错误的HTML布局,例如div块而不是html表等。 –

+0

你检查了哪些组件?我的意思是它们中有很多https://react.parts/web?search=table –

回答

0

我最终的解决方案是:表的使用

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> 
     ); 
    } 
} 

例子:

  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 (
           <button onClick={this.doSomething}> 
           </button> 
          ); 
        } 
       } 
      ]; 

      <Table structure={this.tableStructure} value={this.state.someValue} sortable 
             defaultSortColumn="created" defaultSortOrder={SORT_ORDER.DESC} /> 

的实现是基于http://styleguide.cfapps.io/react_base_tables.html

相关问题