<PaginationContainer
// used in conjunction with internal state: totalCount to calculate pages
pageSize={10}
// if passed, will not invoke onPageChanged on component mount
initialValues={{items: [1,2,3, ...], totalCount: 100, currentPage: 3}}
// function: nextPage => Promise<{items:number, totalCount:number}>
onPageChange={async (nextPage) => {
const {result} = await fetch(nextPage);
return {
items: result.data,
totalCount: result.totalCount,
}
}}
>
{/* Should 'items' be the previous page's items when loading the next page or blank? */}
({loading, items, PaginationComponent}) => (
<FormContainer loading={loading}>
{/* render your items how you want */}
<pre>{JSON.stringify(items)}</pre>
{/* place the PaginationComponent you want */}
{PaginationComponent}
</FormContainer>
)
</PaginationContainer>
Last active
June 7, 2018 20:47
-
-
Save justinobney/66015f7e53805cbc846c0e6b98d80341 to your computer and use it in GitHub Desktop.
Pagination.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, {Component} from 'react'; | |
| import {Menu} from 'semantic-ui-react'; | |
| import segmentize from 'segmentize'; | |
| import min from 'lodash/min'; | |
| import max from 'lodash/max'; | |
| import p from 'prop-types'; | |
| export class PaginationContainer extends Component { | |
| static propTypes = { | |
| render: p.func, | |
| children: p.func, | |
| onPageChange: p.func.isRequired, | |
| pageSize: p.number, | |
| initialValues: p.shape({ | |
| items: p.array.isRequired, | |
| totalCount: p.number.isRequired, | |
| currentPage: p.number.isRequired, | |
| }), | |
| }; | |
| static getDerivedStateFromProps(nextProps, prevState) { | |
| const {initialValuesSet} = prevState; | |
| if (nextProps.initialValues && !initialValuesSet) { | |
| return { | |
| ...nextProps.initialValues, | |
| initialValuesSet: true, | |
| }; | |
| } | |
| return null; | |
| } | |
| state = { | |
| initialValuesSet: false, | |
| loading: false, | |
| error: undefined, | |
| items: [], | |
| totalCount: 0, | |
| currentPage: 1, | |
| }; | |
| componentDidMount() { | |
| if (!this.props.initialValues) { | |
| this._handlePageChanged(this.state.currentPage); | |
| } | |
| } | |
| _handlePageChanged = async page => { | |
| try { | |
| this.setState({loading: true, error: undefined}); | |
| const {items, totalCount} = await this.props.onPageChange(page); | |
| this.setState({items, totalCount, currentPage: page}); | |
| } catch (error) { | |
| this.setState({error}); | |
| } finally { | |
| this.setState({loading: false}); | |
| } | |
| }; | |
| render() { | |
| const {render, children, pageSize = 10} = this.props; | |
| const {loading, error, items, currentPage, totalCount} = this.state; | |
| const renderFn = render || children; | |
| const totalPages = Math.ceil(totalCount / pageSize); | |
| const PaginationComponent = ( | |
| <Pagination | |
| currentPage={currentPage} | |
| totalPages={totalPages} | |
| onPageChange={this._handlePageChanged} | |
| /> | |
| ); | |
| return renderFn({ | |
| items, | |
| currentPage, | |
| totalCount, | |
| loading, | |
| error, | |
| PaginationComponent, | |
| }); | |
| } | |
| } | |
| export class Pagination extends Component { | |
| static propTypes = { | |
| currentPage: p.number.isRequired, | |
| totalPages: p.number.isRequired, | |
| onPageChange: p.func.isRequired, | |
| }; | |
| render() { | |
| const {currentPage = 1, totalPages = 0, onPageChange} = this.props; | |
| if (totalPages === 0) { | |
| return null; | |
| } | |
| const { | |
| beginPages, | |
| previousPages, | |
| centerPage, | |
| nextPages, | |
| endPages, | |
| } = segmentize({ | |
| page: currentPage, | |
| pages: totalPages, | |
| beginPages: 1, | |
| endPages: 1, | |
| sidePages: 2, | |
| }); | |
| const leftGap = min(previousPages) - max(beginPages); | |
| const showLeftGap = !isNaN(leftGap) && leftGap > 1; | |
| const rightGap = min(endPages) - max(nextPages); | |
| const showRightGap = !isNaN(rightGap) && rightGap > 1; | |
| return ( | |
| <Menu pagination> | |
| {beginPages.map(page => ( | |
| <Menu.Item | |
| key={page} | |
| name={`${page}`} | |
| onClick={() => onPageChange(page)} | |
| /> | |
| ))} | |
| {showLeftGap && ( | |
| <Menu.Item key="left gap" icon="ellipsis horizontal" disabled /> | |
| )} | |
| {previousPages.map(page => ( | |
| <Menu.Item | |
| key={page} | |
| name={`${page}`} | |
| onClick={() => onPageChange(page)} | |
| /> | |
| ))} | |
| {centerPage.map(page => ( | |
| <Menu.Item key={page} name={`${page}`} active /> | |
| ))} | |
| {nextPages.map(page => ( | |
| <Menu.Item | |
| key={page} | |
| name={`${page}`} | |
| onClick={() => onPageChange(page)} | |
| /> | |
| ))} | |
| {showRightGap && ( | |
| <Menu.Item key="right gap" icon="ellipsis horizontal" disabled /> | |
| )} | |
| {endPages.map(page => ( | |
| <Menu.Item | |
| key={page} | |
| name={`${page}`} | |
| onClick={() => onPageChange(page)} | |
| /> | |
| ))} | |
| </Menu> | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment