React-table: Total number of records inside the pager.

Created on 20 Sep 2017  路  12Comments  路  Source: tannerlinsley/react-table

Not a bug, but can we have an optional component inside the pager to show the total number of records in the entire list?

Most helpful comment

why close this? This should be a feature request. It would be really useful to see this in the pagination footer: Showing 11-21 of 50 total rows

All 12 comments

This is possible with a custom pagination component. We invite you to join the slack channel and ask for help there or on stack overflow. Thanks!

why close this? This should be a feature request. It would be really useful to see this in the pagination footer: Showing 11-21 of 50 total rows

Not perfect, but here's a very quick pass at modifying the default pagination component to add row counts in the above format:

import React, { Component } from 'react'
import classnames from 'classnames'

const defaultButton = props => (
  <button type="button" {...props} className="-btn">
    {props.children}
  </button>
)

export default class CBReactTablePagination extends Component {
  constructor (props) {
    super()

    this.getSafePage = this.getSafePage.bind(this)
    this.changePage = this.changePage.bind(this)
    this.applyPage = this.applyPage.bind(this)

    this.updateCurrentRows(props)    

    this.state = {
      page: props.page
    }
  }

  componentWillReceiveProps (nextProps) {
    this.setState({ page: nextProps.page })

    this.updateCurrentRows(nextProps)
  }

  updateCurrentRows(props) {
    if (   typeof props.sortedData  !== 'undefined'  //use props.data for unfiltered (all) rows
        && typeof props.page !== 'undefined'
        && typeof props.pageSize !== 'undefined'
    ){
      this.rowCount = props.sortedData.length  //use props.data.length for unfiltered (all) rows
      this.rowMin = props.page * props.pageSize + 1
      this.rowMax = Math.min((props.page + 1) * props.pageSize, this.rowCount)
    }
  }

  getSafePage (page) {
    if (isNaN(page)) {
      page = this.props.page
    }
    return Math.min(Math.max(page, 0), this.props.pages - 1)
  }

  changePage (page) {
    page = this.getSafePage(page)
    this.setState({ page })
    if (this.props.page !== page) {
      this.props.onPageChange(page)
    }

    this.updateCurrentRows(page)
  }

  applyPage (e) {
    if (e) { e.preventDefault() }
    const page = this.state.page
    this.changePage(page === '' ? this.props.page : page)
  }


  render () {
    const {
      // Computed
      pages,
      // Props
      page,
      showPageSizeOptions,
      pageSizeOptions,
      pageSize,
      showPageJump,
      canPrevious,
      canNext,
      onPageSizeChange,
      className,
      PreviousComponent = defaultButton,
      NextComponent = defaultButton,
    } = this.props

    return (
      <div
        className={classnames(className, '-pagination')}
        style={this.props.style}
      >
        <div className="-previous">
          <PreviousComponent
            onClick={() => {
              if (!canPrevious) return
              this.changePage(page - 1)
            }}
            disabled={!canPrevious}
          >
            {this.props.previousText}
          </PreviousComponent>
        </div>
        <div className="-center">
          <span className="-pageInfo">
            {this.props.pageText}{' '}
            {showPageJump
              ? <div className="-pageJump">
                <input
                  type={this.state.page === '' ? 'text' : 'number'}
                  onChange={e => {
                    const val = e.target.value
                    const page = val - 1
                    if (val === '') {
                      return this.setState({ page: val })
                    }
                    this.setState({ page: this.getSafePage(page) })
                  }}
                  value={this.state.page === '' ? '' : this.state.page + 1}
                  onBlur={this.applyPage}
                  onKeyPress={e => {
                    if (e.which === 13 || e.keyCode === 13) {
                      this.applyPage()
                    }
                  }}
                />
              </div>
              : <span className="-currentPage">
                {page + 1}
              </span>}{' '}
            {this.props.ofText}{' '}
            <span className="-totalPages">{pages || 1}</span>
          </span>

          {(typeof this.rowCount !== 'undefined') ?
              <span className="-rowInfo">{"Showing "}
                <span className="-rowMin">{this.rowMin}</span>
                  {" - "}
                <span className="-rowMax">{this.rowMax}</span>
                  {" of "} 
                <span className="-rowCount">{this.rowCount}</span>
                  {" total rows"}
              </span>
            : ''}

          {showPageSizeOptions &&
            <span className="select-wrap -pageSizeOptions">
              <select
                onChange={e => onPageSizeChange(Number(e.target.value))}
                value={pageSize}
              >
                {pageSizeOptions.map((option, i) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <option key={i} value={option}>
                    {option} {this.props.rowsText}
                  </option>
                ))}
              </select>
            </span>}
        </div>
        <div className="-next">
          <NextComponent
            onClick={() => {
              if (!canNext) return
              this.changePage(page + 1)
            }}
            disabled={!canNext}
          >
            {this.props.nextText}
          </NextComponent>
        </div>
      </div>
    )
  }
}

Import the above and wherever you drop in a react-table just add
PaginationComponent={CBReactTablePagination} or whatever you want to call it

@Morasta - Thanks for sharing your script. That saved me a lot of time in my own implementation.

Since I am using filters on all of my columns, I wanted to output how many rows are actually showing on screen.

Thus, I changed one item in your updateCurrentRows method:

   updateCurrentRows(props) {
        if (   typeof props.data  !== 'undefined'
            && typeof props.page !== 'undefined'
            && typeof props.pageSize !== 'undefined'
        ){
            this.rowCount = props.sortedData.length
            this.rowMin = props.page * props.pageSize + 1
            this.rowMax = Math.min((props.page + 1) * props.pageSize, this.rowCount)
        }
    }

I am using "sortedData" array instead of the "data" array in this method. That array, at least as far as I can tell, contains only the filtered / sorted data.

This appears to do the trick if you only want the count of rows currently available under your filters.

Thanks again!

That's awesome, @aldefouw. I am also using filters in a couple places, thanks for sharing the change you made to use filtered counts! That'll be useful on my end.

I think maybe the first if check in your edit should be props.sortedData to make sure there's data in there (no results may cause errors?).

@Morasta - Thanks - glad it will be helpful.

I can definitely understand the logic of wanting to check against props.sortedData. I am very new to React Table so I cannot speak definitively on whether that would make a difference.

Is there ever a case where props.sortedData would be undefined but props.data wouldn't?

I haven't hit that case yet but I can see where the code would seem to make sense more if we were checking against sortedData.

Thanks to both @Morasta and @aldefouw for their work, very useful to me on a tight deadline here. Also very new to React-Table so this was a great example of how it can be extended.

@Morasta : Above custom pagination example was really helpful. Thank you.

Could you please let me know, if it's possible to add First & Last button functionality as well?
Because I tried to add 2 components for first and last buttons (similar to Next /Previous), but because of '-next' & '-previous' classes, and canNext & canPrevious, I have no idea how to add button texts & those flags. Because button texts are blank & no values for canFirst & canLast.
Could you please advice on this.
Thank you.

@dilushi : Glad you found it helpful!

Here's a rough addition of first and last buttons. I repurposed the Previous and Next components and changed which page they reference for navigation.

import React, { Component } from 'react'
import classnames from 'classnames'
// import _ from './utils'

/*
  Custom react table pagination with row count support
  Based on core component: https://github.com/react-tools/react-table/blob/master/src/pagination.js
*/

const defaultButton = props => (
  <button type="button" {...props} className="-btn">
    {props.children}
  </button>
)

export default class CBReactTablePagination extends Component {
  constructor (props) {
    super()

    this.getSafePage = this.getSafePage.bind(this)
    this.changePage = this.changePage.bind(this)
    this.applyPage = this.applyPage.bind(this)

    this.updateCurrentRows(props)    

    this.state = {
      page: props.page
    }
  }

  componentWillReceiveProps (nextProps) {
    this.setState({ page: nextProps.page })

    this.updateCurrentRows(nextProps)
  }

  updateCurrentRows(props) {
    if (   typeof props.sortedData  !== 'undefined'
        && typeof props.page !== 'undefined'
        && typeof props.pageSize !== 'undefined'
    ){
      this.rowCount = props.sortedData.length
      this.rowMin = props.page * props.pageSize + 1
      this.rowMax = Math.min((props.page + 1) * props.pageSize, this.rowCount)
    }
  }

  getSafePage (page) {
    if (isNaN(page)) {
      page = this.props.page
    }
    return Math.min(Math.max(page, 0), this.props.pages - 1)
  }

  changePage (page) {
    page = this.getSafePage(page)
    this.setState({ page })
    if (this.props.page !== page) {
      this.props.onPageChange(page)
    }

    this.updateCurrentRows(page)
  }

  applyPage (e) {
    if (e) { e.preventDefault() }
    const page = this.state.page
    this.changePage(page === '' ? this.props.page : page)
  }


  render () {
    const {
      // Computed
      pages,
      // Props
      page,
      showPageSizeOptions,
      pageSizeOptions,
      pageSize,
      showPageJump,
      canPrevious,
      canNext,
      onPageSizeChange,
      className,
      PreviousComponent = defaultButton,
      NextComponent = defaultButton,
    } = this.props

    return (
      <div
        className={classnames(className, '-pagination')}
        style={this.props.style}
      >
        <div className="-first">
          <PreviousComponent
            onClick={() => {
              if (!canPrevious) return
              this.changePage(0)
            }}
            disabled={!canPrevious}
          >
            {'|<'}
          </PreviousComponent>
        </div>
        <div className="-previous">
          <PreviousComponent
            onClick={() => {
              if (!canPrevious) return
              this.changePage(page - 1)
            }}
            disabled={!canPrevious}
          >
            {this.props.previousText}
          </PreviousComponent>
        </div>
        <div className="-center">
          <span className="-pageInfo">
            {this.props.pageText}{' '}
            {showPageJump
              ? <div className="-pageJump">
                <input
                  type={this.state.page === '' ? 'text' : 'number'}
                  onChange={e => {
                    const val = e.target.value
                    const page = val - 1
                    if (val === '') {
                      return this.setState({ page: val })
                    }
                    this.setState({ page: this.getSafePage(page) })
                  }}
                  value={this.state.page === '' ? '' : this.state.page + 1}
                  onBlur={this.applyPage}
                  onKeyPress={e => {
                    if (e.which === 13 || e.keyCode === 13) {
                      this.applyPage()
                    }
                  }}
                />
              </div>
              : <span className="-currentPage">
                {page + 1}
              </span>}{' '}
            {this.props.ofText}{' '}
            <span className="-totalPages">{pages || 1}</span>
          </span>

          {(typeof this.rowCount !== 'undefined') ?
              <span className="-rowInfo">{"Showing "}
                <span className="-rowMin">{this.rowMin}</span>
                  {" - "}
                <span className="-rowMax">{this.rowMax}</span>
                  {" of "} 
                <span className="-rowCount">{this.rowCount}</span>
                  {" total rows"}
              </span>
            : ''}

          {showPageSizeOptions &&
            <span className="select-wrap -pageSizeOptions">
              <select
                onChange={e => onPageSizeChange(Number(e.target.value))}
                value={pageSize}
              >
                {pageSizeOptions.map((option, i) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <option key={i} value={option}>
                    {option} {this.props.rowsText}
                  </option>
                ))}
              </select>
            </span>}
        </div>
        <div className="-next">
          <NextComponent
            onClick={() => {
              if (!canNext) return
              this.changePage(page + 1)
            }}
            disabled={!canNext}
          >
            {this.props.nextText}
          </NextComponent>
        </div>
        <div className="-last">
          <NextComponent
            onClick={() => {
              if (!canNext) return
              this.changePage(pages)
            }}
            disabled={!canNext}
          >
            {'>|'}
          </NextComponent>
        </div>
      </div>
    )
  }
}

And some quick and dirty CSS to get ya started:

/* Pagination */
.ReactTable .-pagination .-first {
    margin-right: 3px;    
}
.ReactTable .-pagination .-last {
    margin-left: 3px;    
}

please let me know, how can i added optional totalPages ?
example server response :
{
data: [
{id: 0},{id: 1},{id: 2},{id: 3},{id: 4},{id: 5}
],
pages: { totalPages: 10 }
}
i want show "Page 1 of 10" with 5 rows 5 records but i can't custom totalPages

@dilushi : Glad you found it helpful!

Here's a rough addition of first and last buttons. I repurposed the Previous and Next components and changed which page they reference for navigation.

import React, { Component } from 'react'
import classnames from 'classnames'
// import _ from './utils'

/*
  Custom react table pagination with row count support
  Based on core component: https://github.com/react-tools/react-table/blob/master/src/pagination.js
*/

const defaultButton = props => (
  <button type="button" {...props} className="-btn">
    {props.children}
  </button>
)

export default class CBReactTablePagination extends Component {
  constructor (props) {
    super()

    this.getSafePage = this.getSafePage.bind(this)
    this.changePage = this.changePage.bind(this)
    this.applyPage = this.applyPage.bind(this)

    this.updateCurrentRows(props)    

    this.state = {
      page: props.page
    }
  }

  componentWillReceiveProps (nextProps) {
    this.setState({ page: nextProps.page })

    this.updateCurrentRows(nextProps)
  }

  updateCurrentRows(props) {
    if (   typeof props.sortedData  !== 'undefined'
        && typeof props.page !== 'undefined'
        && typeof props.pageSize !== 'undefined'
    ){
      this.rowCount = props.sortedData.length
      this.rowMin = props.page * props.pageSize + 1
      this.rowMax = Math.min((props.page + 1) * props.pageSize, this.rowCount)
    }
  }

  getSafePage (page) {
    if (isNaN(page)) {
      page = this.props.page
    }
    return Math.min(Math.max(page, 0), this.props.pages - 1)
  }

  changePage (page) {
    page = this.getSafePage(page)
    this.setState({ page })
    if (this.props.page !== page) {
      this.props.onPageChange(page)
    }

    this.updateCurrentRows(page)
  }

  applyPage (e) {
    if (e) { e.preventDefault() }
    const page = this.state.page
    this.changePage(page === '' ? this.props.page : page)
  }


  render () {
    const {
      // Computed
      pages,
      // Props
      page,
      showPageSizeOptions,
      pageSizeOptions,
      pageSize,
      showPageJump,
      canPrevious,
      canNext,
      onPageSizeChange,
      className,
      PreviousComponent = defaultButton,
      NextComponent = defaultButton,
    } = this.props

    console.log("pagination props")
    console.log(this.props)

    return (
      <div
        className={classnames(className, '-pagination')}
        style={this.props.style}
      >
        <div className="-first">
          <PreviousComponent
            onClick={() => {
              if (!canPrevious) return
              this.changePage(0)
            }}
            disabled={!canPrevious}
          >
            {'|<'}
          </PreviousComponent>
        </div>
        <div className="-previous">
          <PreviousComponent
            onClick={() => {
              if (!canPrevious) return
              this.changePage(page - 1)
            }}
            disabled={!canPrevious}
          >
            {this.props.previousText}
          </PreviousComponent>
        </div>
        <div className="-center">
          <span className="-pageInfo">
            {this.props.pageText}{' '}
            {showPageJump
              ? <div className="-pageJump">
                <input
                  type={this.state.page === '' ? 'text' : 'number'}
                  onChange={e => {
                    const val = e.target.value
                    const page = val - 1
                    if (val === '') {
                      return this.setState({ page: val })
                    }
                    this.setState({ page: this.getSafePage(page) })
                  }}
                  value={this.state.page === '' ? '' : this.state.page + 1}
                  onBlur={this.applyPage}
                  onKeyPress={e => {
                    if (e.which === 13 || e.keyCode === 13) {
                      this.applyPage()
                    }
                  }}
                />
              </div>
              : <span className="-currentPage">
                {page + 1}
              </span>}{' '}
            {this.props.ofText}{' '}
            <span className="-totalPages">{pages || 1}</span>
          </span>

          {(typeof this.rowCount !== 'undefined') ?
              <span className="-rowInfo">{"Showing "}
                <span className="-rowMin">{this.rowMin}</span>
                  {" - "}
                <span className="-rowMax">{this.rowMax}</span>
                  {" of "} 
                <span className="-rowCount">{this.rowCount}</span>
                  {" total rows"}
              </span>
            : ''}

          {showPageSizeOptions &&
            <span className="select-wrap -pageSizeOptions">
              <select
                onChange={e => onPageSizeChange(Number(e.target.value))}
                value={pageSize}
              >
                {pageSizeOptions.map((option, i) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <option key={i} value={option}>
                    {option} {this.props.rowsText}
                  </option>
                ))}
              </select>
            </span>}
        </div>
        <div className="-next">
          <NextComponent
            onClick={() => {
              if (!canNext) return
              this.changePage(page + 1)
            }}
            disabled={!canNext}
          >
            {this.props.nextText}
          </NextComponent>
        </div>
        <div className="-last">
          <NextComponent
            onClick={() => {
              if (!canNext) return
              this.changePage(pages)
            }}
            disabled={!canNext}
          >
            {'>|'}
          </NextComponent>
        </div>
      </div>
    )
  }
}

And some quick and dirty CSS to get ya started:

/* Pagination */
.ReactTable .-pagination .-first {
    margin-right: 3px;    
}
.ReactTable .-pagination .-last {
    margin-left: 3px;    
}

Thanks, this code is very useful.

Don't you want to add this feature? It's still needed by users

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dwjft picture dwjft  路  3Comments

DaveyEdwards picture DaveyEdwards  路  3Comments

alexanderwhatley picture alexanderwhatley  路  3Comments

bdkersey picture bdkersey  路  3Comments

danielmariz picture danielmariz  路  3Comments