添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

This version has been deprecated

Author message:

no longer supported

react-data-table-component
TypeScript icon, indicating that this package has built-in type declarations

3.0.4 Public • Published

Build Status npm version codecov Storybook

React Data Table Component

Creating yet another React table library came out of necessity while developing a web application for a growing startup. I discovered that while there are some great table libraries already available, most required heavy customization or lacked basic features such as built in sorting and pagination, and in some cases required a restrictive license.

If you want to achieve balance with the force and want a simple but flexible table library give React Data Table Component a chance. If you require an Excel clone and need to pivot large data sets then this is not the React table library you are looking for 👋

Table of Contents

  • Features
  • Demo & Examples
  • Requirements
  • Installation
  • Issues/Contributing
  • Defining Columns
  • DataTable Properties
  • Basic Table
  • Selectable Rows
  • Custom Cells
  • Expandable Rows
  • Optimizing for Performance and Caveats
  • Theming
  • CSS Overrides
  • Demo and Examples

    React Data Table Component Demo

    Key Features

  • Declarative Configuration
  • Sortable (client)
  • Selectable Rows
  • Expandable Rows
  • Themeable via js config
  • Data Aware (i.e. easily callback to a parent component get the DataTable state, e.g. selectedRows
  • Responsive (via x-scroll/flex)
  • Pagination
  • Requirements

    React Data Table Component requires the following be installed in your project:

  • React 16.8.0+
  • styled-components 3.2.3+ || 4.0.0+ || 5.0.0+
  • Installation

    React Data Table requires the wonderful styled-components library. If you've already installed styled-components there is no need to install it again.

    npm install react-data-table-component styled-components
    
    yarn add react-data-table-component styled-components
    

    Logging Issues and Contributions

    Please use the github issue templates feature for logging issues or feature proposals. Including a codesanbox and providing clear details on the feature/issue will elicit a much quicker response 😉

    API and Usage

    Columns

    Nothing new here - we are using an array of object literals and properties to describle the columns:

    Property Required Example for ultimate control use cell to render your own custom component! e.g row => <h2>{row.title}</h2> Negates format number flex-grow of the column. the is useful if you want a column to take up more width than its relatives (without having to set widths explicitly). this will be affected by other columns where you have explicitly set widths width string give the column a fixed width minWidth string give the column a minWidth maxWidth string give the column a maxWidth right right aligns the content in the cell. useful for numbers center center aligns the content in the cell compact reduces the padding in the cell by 50% button applies additional styling when using a button whether the cell content should be allowed to wrap. allowOverflow allows content in the cell to overflow. useful for menus/layovers that do not rely on "smart" positioning ignoreRowClick prevents the onRowClicked event from being passed on a specific TableCell column. This is really useful for a menu or button where you do not want the onRowClicked triggered integer or string preset (sm, md, lg) specify a screen size (breakpoint) as an integer (in pixels) that hides the column when resizing the browser window. You can also use the preset values of: sm (small), md(medium), and lg(large)

    column.hide media presets

    When the breakpoint is reached the column will be hidden. These are the built-in media breakpoint presets when hiding columns

    Value Breakpoint Description it is highly recommended that your data has a unique identifier (keyField). The default keyField is id. If you need to override this value then see keyField DataTable Properties. keyField string your data should have a unique identifier. By default, React Data Table looks for an id property for each item in your data. You must match keyField to your identifier key, especially if you want to manage row state at a later time or use the expander feature. If a unique id is not present, React Data Table will use the row index (not recommended) as the key value striped false stripe color the odd rows highlightOnHover false if rows are to be highlighted on hover pointerOnHover false if rows show a point icon on hover noDataComponent string or component A custom component to display when there are no records to display className string override the className on the Table wrapper style object override the style on the Table wrapper responsive makes the table horizontally scrollable on smaller screen widths customTheme object Override the default theme, by overriding specifc props. Your changes will be merged. See Theming for more information disabled false disables the Table section onRowClicked callback to access the row data,index on row click overflowY false if a table is responsive, items such as layovers/menus/dropdowns will be clipped on the last row(s) due to to overflow-x-y behavior - setting this value ensures there is invisible space below the table to prevent "clipping". However, if possible, the correct approach is to use menus/layovers/dropdowns that support smart positioning. If used, the table parent element must have a fixed height or height: 100%. overflowYOffset string 250px used with overflowY to "fine tune" the offset

    Progress Indicator

    Property Required Default Description false toggling this property clears the selectedRows. If you use redux or react state you need to make sure that you pass a toggled value or the component will not update. See Clearing Selected Rows onRowSelected callback to access the row selected state ({ allSelected, selectedCount, selectedRows }). It's highly recommended that you memoize any handlers that you pass to onRowSelected to prevent DataTable from unnescessary re-renders selectableRowsComponent Override the default checkbox component - must be passed as a function (e.g. Checkbox not <Checkbox />) selectableRowsComponentProps object Additional props you want to pass to selectableRowsComponent. See Advanced Selectable Component Options to learn how you can override indeterminate state

    Row Expander

    Property Required Default Description false Whether to make a row expandable, if true it requires an expandableRowsComponent. It is highly recommended your data set have a unique identifier defined as the keyField for row expansion to work properly. expandableIcon object default expander icons you may pass in your own custom icons using the expandableIcon: { collapsed: <svg>...</svg>, expanded: <svg>...</svg> expandableDisabledField string React Data Table looks for this property in each item from your data and checks if that item can be expanded or not. You must set a bool value in the expandableDisabledField of your data if you want to use this feature. defaultExpandedField string React Data Table looks for this property in each item from your data and checks if that item should be expanded on initial render. You must set a bool value in the defaultExpandedField field of your data if you want to use this feature. expandableRowsComponent string or component A custom component to display in the expanded row. It will have the data prop composed so that you may access the row data

    Sorting

    Property Required Default Description Setting this ensures the table data is presorted before it renders and the field(selector) is focused defaultSortAsc set this to false if you want the table data to be sorted in DESC order sortIcon component Override the default sort icon - the icon must be a font or svg icon and it should be a "downward" icon since animation will be handled by React Data Table onSort callback to access the sort state when a column is clicked. returns (column, sortDirection, event) sortFunction pass in your own custom sort function e.g. `(rows, field, direction) => {...yourSortLogicHere}. you must return an array

    Pagination

    Property Required Default Description false enable pagination with defaults. by default the total record set will be sliced depending on the page, rows per page. if you wish to use server side pagination then use the paginationServer property paginationServer false changes the default pagination to work with server side pagination paginationDefaultPage number the default page to use when the table initially loads paginationTotalRows number allows you to provide the total row count for your table as represented by your API when performing server side pagination. if this property is not provided then react-data-table will use data.length paginationPerPage number the default rows per page to use when the table initially loads paginationRowsPerPageOptions number [10, 15, 20, 25, 30] row page dropdown selection options onChangePage callback when paged that returns onChangePage(page, totalRows) onChangeRowsPerPage callback when rows per page is changed returns onChangeRowsPerPage(currentRowsPerPage, currentPage) paginationComponent Pagination a component that overrides the default paginator component paginationComponentOptions object See Description overridable options for the built in pagination component. If you are developing a custom pagination component you can use paginationComponentOptions to pass in your own custom props. Defaults to: { rowsPerPageText: 'Rows per page:', rangeSeparatorText: 'of' } paginationIconFirstPage a component that overrides the first page icon for the pagination paginationIconLastPage a component that overrides the last page icon for the pagination paginationIconNext a component that overrides the next page icon for the pagination paginationIconPrevious a component that overrides the previous page icon for the pagination

    Header

    Property Required Default Description 100vh in order for fixedHeader to work this property allows you to set a static height to the TabelBody. height must be a fixed value subHeader component or array of components false show a subheader between the table and table header subHeaderAlign string right align the subheader content (left, right, center) subHeaderWrap whether the subheader content should wrap subHeaderComponent component or array of components a component you want to render

    Advanced Selectable Component Options

    Sometimes 3rd party checkbox components have their own way of handling indeterminate state. We don't want React Data Table hardcoded to a specific ui lib or custom component, so instead a "hook" is provided to allow you to pass a function that will be resolved by React Data Table's internal Checkbox for use with indeterminate functionality.

    Example Usage:

     
    const { Checkbox } from 'react-md';
     
    ...
     
    /*
      In this example, the react-md ui lib determines its own indeterminate state via the `uncheckedIcon` property.
      Let's override it. React Data Table is made aware if a checkbox is indetermite or not becuase internally we can resolve this   as `yourfunction(checkboxawareindeterminatestate)`
    */
     
    const handleIndeterminate = isIndeterminate => (isIndeterminate ? <FontIcon>indeterminate_check_box</FontIcon> : <FontIcon>check_box_outline_blank</FontIcon>);
     
    const MyComponent = () => (
      <DataTable
        title="Arnold Movies"
        columns={columns}
        data={data}
        selectableRows
        selectableRowsComponent={Checkbox} // Pass the function only
        selectableRowsComponentProps={{ uncheckedIcon: handleIndeterminate  }}
      />
    );

    Note This is currently only supported for indeterminate state, but may be expanded in the future if there is a demand

    Basic Table

    The following declarative structure creates a sortable table of Arnold movie titles:

    import DataTable from 'react-data-table-component';
     
    const data = [{ id: 1, title: 'Conan the Barbarian', year: '1982' } ...];
    const columns = [
      {
        name: 'Title',
        selector: 'title',
        sortable: true,
      },
      {
        name: 'Year',
        selector: 'year',
        sortable: true,
        right: true,
      },
    ];
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            data={data}
          />
        )
      }
    );
     

    Selectable Rows

    Let's make our rows selectable so we can access the selected results

    ...
     
    const handleChange = (state) => {
      // You can use setState or dispatch with something like Redux so we can use the retrieved data
      console.log('Selected Rows: ', state.selectedRows);
    };
     
    class MyComponent extends Component {
      render() {
          return (
            <DataTable
              title="Arnold Movies"
              columns={columns}
              data={data}
              selectableRows // add for checkbox selection
              onRowSelected={handleChange}
            />
        )
      }
    );

    Clearing Selected Rows

    We need some hook to trigger all the selectedRows to clear. If you were building your own table component, you would manage the selected rows state in some parent component, however, in our case, since we to keep row management within React Data Table, a clearSelectedRows prop is provided so you can pass a toggled state.

    It will be up to you to make sure you do not pass the same state twice. For example, if you set clearSelectedRows={true} twice, on the second update/trigger, none the rows will not be cleared.

    ...
    // set the initial state
    state = { toggledClearRows: false }
    ...
     
    const handleChange = (state) => {
      // You can use setState or dispatch with something like Redux so we can use the retrieved data
      console.log('Selected Rows: ', state.selectedRows);
    };
     
    // Toggle the state so React Table Table changes to `clearSelectedRows` are triggered
    const handleClearRows = () => {
      this.setState({ toggledClearRows: !this.state.toggledClearRows})
    }
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            data={data}
            selectableRows // add for checkbox selection
            onRowSelected={handleChange}
            clearSelectedRows={this.state.toggledClearRows}
          />
        )
      }
    );

    Overriding with a 3rd Party Ui Component Library

    Don't like those ugly html checkboxes? Let's override them with some react-md sexyiness. While we are at it we will also override the sortIcon:

    ...
    import { Checkbox, FontIcon } from 'react-md';
    ...
     
    class MyComponent extends Component {
      render() {
        return (
          title="Arnold Movies"
          columns={columns}
          data={data}
          selectableRows
          selectableRowsComponent={Checkbox} // Pass the function only
          selectableRowsComponentProps={{ inkDisabled: true }} // optionally, pass react-md supported props down to our custom checkbox
          sortIcon={<FontIcon>arrow_downward</FontIcon>} // use a material icon for our sort icon. rdt will rotate the icon 180 degrees
          onRowSelected={handleChange}
        />
        )
      }
    );

    Custom Cells

    Let's give our Movie list a summary, but in the same cell as Name:

    ....
     
    const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982' } ...];
    const columns = [
      {
        name: 'Title',
        sortable: true,
        cell: row => <div><div style={{ fontWeight: bold }}>{row.title}</div>{row.summary}</div>,
      },
      {
        name: 'Year',
        selector: 'year',
        sortable: true,
        right: true,
      },
    ];
     
    ...
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            data={data}
            selectableRows
            selectableRowsComponent={Checkbox}
            selectableRowsComponentProps={{ inkDisabled: true }}
            sortIcon={<FontIcon>arrow_downward</FontIcon>}
            onRowSelected={handleChange}
          />
        )
      }
    );

    Expandable Rows

    Let's make our rows expandable so we can view more details:

    ...
     
    const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982', image: 'http://conan.image.png' } ...];
    const columns = [
      {
        name: 'Title',
        sortable: true,
        cell: row => <div><div style={{ fontWeight: 700 }}>{row.title}</div>{row.summary}</div>,
      },
      {
        name: 'Year',
        selector: 'year',
        sortable: true,
        right: true,
      },
    ];
     
    ...
     
    // The row data is composed into your custom expandable component via the data prop
    const ExpanableComponent = ({ data }) => <img src={data.image} />;
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            data={data}
            selectableRows
            selectableRowsComponent={Checkbox}
            selectableRowsComponentProps={{ inkDisabled: true }}
            sortIcon={<FontIcon>arrow_downward</FontIcon>}
            onRowSelected={handleChange}
            expandableRows
            expandableRowsComponent={<ExpanableComponent />}
          />
        )
      }
    );

    But in some cases we don't have more details to show:

    ...
     
    const data = [{ id: 1, title: 'Conan the Barbarian', summary: 'Orphaned boy Conan is enslaved after his village is destroyed...',  year: '1982', expanderDisabled: true, image: 'http://conan.image.png' } ...];
    const columns = [
      {
        name: 'Title',
        sortable: true,
        cell: row => <div><div style={{ fontWeight: 700 }}>{row.title}</div>{row.summary}</div>,
      },
      {
        name: 'Year',
        selector: 'year',
        sortable: true,
        right: true,
      },
    ];
     
    ...
     
    // The row data is composed into your custom expandable component via the data prop
    const ExpanableComponent = ({ data }) => <img src={data.image} />;
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            data={data}
            selectableRows
            selectableRowsComponent={Checkbox}
            selectableRowsComponentProps={{ inkDisabled: true }}
            sortIcon={<FontIcon>arrow_downward</FontIcon>}
            onRowSelected={handleChange}
            expandableRows
            expandableDisabledField="expanderDisabled"
            expandableRowsComponent={<ExpanableComponent />}
          />
        )
      }
    );

    Optimizing for Performance and Caveats

    Pre-optimizaton can be the root of all evil, however, there are some best practices you can adhere to that will ensure React Data Table (RDT) is giving you the performance that you expect.

    Passing non-primitive props (objects, arrays and functions)

    While RDT has internal optimizations to try and prevent re-renders on deeper internal components, for peak performance it's up to you to make sure that you understand how React manages rendering when props/state change as well as how JavaScript determines equality for non-primitives. As a general rule, or if you are experiencing performance issues you should ensure that any non-primitive property that's passed into RDT is not re-created on every render cycyle. This is even more important when you have larger data sets or you are passing complex components and columns to DataTable.

    Optimizing Class Components

    You can typically achieve this by moving props such as objects, arrays, functions or other React compents that you pass to RDT outside of the render method. Additionally, RDT provides you with a memoize helper for cases where you are using a function to generate those values.

    Examples of Optimizations

    The following component will cause RDT to fully re-render everytime onRowSelected is triggered. Why? Becuase when setState is called it triggers myComponent to re-render which by design triggers a re-render on all child components i.e. DataTable. But luckily for you React optimally handles this descision on when and how to re-render DataTable and a full re-render should not occur as long as DataTable props are the same.

    However, in the example below columns changes on every re-render becuase it's being re-created. This is due to referential equality checking, simply: columns[] !== columns[]. In other words, while both instances of columns contain the same elements, they are "different" arrays.

    ...
    import React, { Component } from 'react';
    import DataTable from 'react-data-table';
     
    class MyComponent extends Component {
      updateState = state => {
        this.setState({ selectedRows: state.selectedRows }); // triggers MyComponet to re-render with new state
      }
     
      render () { // by design runs on every setState trigger
        // upon re-render columns array is recreated and thus causes DataTable to re-render
        const columns = [....];
     
        return (
          <DataTable
            data={data}
            columns={columns}
            onRowSelected={this.updateState}
            selectableRows
          />
        )
      }
    }

    A "solution" could be to declare any field that is a non primitive field outside of the render function so that it does not get recreated on every re-render cycle:

    ...
    import React, { Component } from 'react';
    import DataTable from 'react-data-table';
     
    const columns = [....]; // is only created once
     
    class MyComponent extends Component {
      updateState = state => {
        this.setState({ selectedRows: state.selectedRows });
      }
     
      render () {
     
        return (
          <DataTable
            data={data}
            columns={columns}
            onRowSelected={this.updateState}
            selectableRows
          />
        )
      }
    }
     

    But that only works if you don't need to pass component props/methods to the column object. For example what if you want to attach an event handler to a button in the row using column.cell?

    const columns = [;
      {
        cell: () => <Button raised primary onClick={this.handleAction}>Action</Button>,
        ignoreRowClick: true,
        allowOverflow: true,
        button: true,
      },
      ...
    ];

    So how do we attach event handlers to our columns without having to place it in the render method and dealing with unnescessary re-renders?

  • Create a columns function and pass the arguments needed
  • Memoize the columns function
  • This way, when React checks if columns has changes columns will instead be cached result (remember referential equality), thus no unnessesary re-render.

    Got it? Let's try this again with the optimal solution:

    import React, { Component } from 'react';
    import DataTable, { memoize } from 'react-data-table';
     
    const onRowSelected = memoize(handleAction => [
      ...
      {
        cell: () => <Button raised primary onClick={handleAction}>Action</Button>,
        ignoreRowClick: true,
        allowOverflow: true,
        button: true,
      },
      ...
    ]);
     
    class MyComponent extends Component {
      updateState = state => {
        this.setState({ selectedRows: state.selectedRows });
      }
     
      render () {
        return (
          <DataTable
            data={data}
            columns={columns(this.updateState)}
            onRowSelected={this.updateState}
            selectableRows
          />
        );
      }
    }

    Notice that this.updateState does not require memoization. That's because this.updateState is defined as a class method and therefore only created once. This however, is a different matter with function components.

    Optimizing Functional Components

    If you're building functional components you get access to React Hooks such as useMemo and useCallback. In this example, simply wrap columns in a useMemo callback and your updateState into useCallback:

    import React, { useState, useMemo } from 'react';
    import DataTable from 'react-data-table';
     
    const MyComponentHook = () => {
      const [thing, setThing] = useState();
      const handleAction = value => setThing(value);
      // unklike class methods updateState will be re-created on each render pass, therefore, make sure that callbacks passed to onRowSelected are memoized using useCallback
      const updateState = useCallback(state => console.log(state));
      const columns = useMemo(() => [
        ...
        {
          cell: () => <Button raised primary onClick={handleAction}>Action</Button>,
          ignoreRowClick: true,
          allowOverflow: true,
          button: true,
        },
        ...
      ]);
     
      return (
        <DataTable
          data={data}
          columns={columns}
          onRowSelected={updateState}
          selectableRows
        />
      );
    }

    Theming

    You can override or replace the default theme using the customTheme prop. Internally, this just deep merges your theme with the default theme.

    For Example:

    // Override the row default height
    const mySweetTheme = {
      rows: {
        height: '64px'
      }
    }
     
    class MyComponent extends Component {
      render() {
        return (
          <DataTable
            title="Arnold Movies"
            columns={columns}
            customTheme={mySweetTheme}
          />
        )
      }
    );

    Refer to Default Theme for reference and avaiilable properties to override

    CSS Overrides

    If you would like to customize the layout components of React Data Table using styled-components (e.g. styled(DataTable)), or your favorite CSS, SCSS, LESS, etc.. preprocesor you may use the following classNames:

  • rdt_Table
  • rdt_TableRow
  • rdt_TableCol
  • rdt_TableCol_Sortable
  • rdt_TableCell
  • rdt_TableHeader
  • rdt_TableFooter
  • rdt_TableHead
  • rdt_TableHeadRow
  • rdt_TableBody
  • rdt_ExpanderRow
  • Development

    Setup

    Install the latest Node JS LTS and Yarn and simply run yarn or yarn install command in the root and stories directory.

    It is advised to run the script whenever NPM packages are installed.

    Local development

    During development,

    # watch and build new source changes 
    yarn start
    # or serve *.stories.js files and manually test on the Storybook app 
    yarn storybook

    Including NPM packages

    This project uses two package.json structure.

    Library dependencies -- <root_dir>/package.json

    yarn add [package-name] --dev # for dev tools 
    yarn add [package-name] # for app 

    Storybook dependencies -- <root_dir>/stories/package.json

    cd stories/
    yarn add [package-name]
    yarn lint # runs linter to detect any style issues (css & js) 
    yarn lint:css # lint only css 
    yarn lint:js # lint only js 
    yarn lint:js --fix # tries to fix js lint issues 
    yarn test # runs functional/unit tests using Jest 
    yarn test --coverage # with coverage 

    Build