adding local pagination to bucket list (#5684)

* adding local pagination to bucket list

When there are more than 5000 buckets, browser ui
becomes unresponsive since react needs to create
5000 elements which takes browser resources.
So we show only 100 buckets for the first time,
and load more buckets when the user is scrolling down.

* move inline styles to less file
This commit is contained in:
Kanagaraj M 2018-03-22 23:28:48 +05:30 committed by GitHub
parent 1459c4be1e
commit cb3818be27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 14 deletions

View File

@ -17,14 +17,28 @@
import React from "react" import React from "react"
import { connect } from "react-redux" import { connect } from "react-redux"
import { Scrollbars } from "react-custom-scrollbars" import { Scrollbars } from "react-custom-scrollbars"
import InfiniteScroll from "react-infinite-scroller"
import * as actionsBuckets from "./actions" import * as actionsBuckets from "./actions"
import { getVisibleBuckets } from "./selectors" import { getFilteredBuckets } from "./selectors"
import BucketContainer from "./BucketContainer" import BucketContainer from "./BucketContainer"
import web from "../web" import web from "../web"
import history from "../history" import history from "../history"
import { pathSlice } from "../utils" import { pathSlice } from "../utils"
export class BucketList extends React.Component { export class BucketList extends React.Component {
constructor(props) {
super(props)
this.state = {
page: 1
}
}
componentWillReceiveProps(nexProps) {
if (this.props.filter != nexProps.filter) {
this.setState({
page: 1
})
}
}
componentWillMount() { componentWillMount() {
const { fetchBuckets, setBucketList, selectBucket } = this.props const { fetchBuckets, setBucketList, selectBucket } = this.props
if (web.LoggedIn()) { if (web.LoggedIn()) {
@ -39,13 +53,29 @@ export class BucketList extends React.Component {
} }
} }
} }
loadNextPage() {
this.setState({
page: this.state.page + 1
})
}
render() { render() {
const { visibleBuckets } = this.props const { filteredBuckets } = this.props
const visibleBuckets = filteredBuckets.slice(0, this.state.page * 100)
return ( return (
<div className="buckets__list"> <div className="buckets__list">
{visibleBuckets.map(bucket => ( <InfiniteScroll
<BucketContainer key={bucket} bucket={bucket} /> pageStart={0}
))} loadMore={this.loadNextPage.bind(this)}
hasMore={filteredBuckets.length > visibleBuckets.length}
useWindow={false}
element="div"
initialLoad={false}
className="buckets__scroll"
>
{visibleBuckets.map(bucket => (
<BucketContainer key={bucket} bucket={bucket} />
))}
</InfiniteScroll>
</div> </div>
) )
} }
@ -53,7 +83,8 @@ export class BucketList extends React.Component {
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
visibleBuckets: getVisibleBuckets(state), filteredBuckets: getFilteredBuckets(state),
filter: state.buckets.filter
} }
} }
@ -61,7 +92,7 @@ const mapDispatchToProps = dispatch => {
return { return {
fetchBuckets: () => dispatch(actionsBuckets.fetchBuckets()), fetchBuckets: () => dispatch(actionsBuckets.fetchBuckets()),
setBucketList: buckets => dispatch(actionsBuckets.setList(buckets)), setBucketList: buckets => dispatch(actionsBuckets.setList(buckets)),
selectBucket: bucket => dispatch(actionsBuckets.selectBucket(bucket)), selectBucket: bucket => dispatch(actionsBuckets.selectBucket(bucket))
} }
} }

View File

@ -14,25 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
import { getVisibleBuckets, getCurrentBucket } from "../selectors" import { getFilteredBuckets, getCurrentBucket } from "../selectors"
describe("getVisibleBuckets", () => { describe("getFilteredBuckets", () => {
let state let state
beforeEach(() => { beforeEach(() => {
state = { state = {
buckets: { buckets: {
list: ["test1", "test11", "test2"], list: ["test1", "test11", "test2"]
}, }
} }
}) })
it("should return all buckets if no filter specified", () => { it("should return all buckets if no filter specified", () => {
state.buckets.filter = "" state.buckets.filter = ""
expect(getVisibleBuckets(state)).toEqual(["test1", "test11", "test2"]) expect(getFilteredBuckets(state)).toEqual(["test1", "test11", "test2"])
}) })
it("should return all matching buckets if filter is specified", () => { it("should return all matching buckets if filter is specified", () => {
state.buckets.filter = "test1" state.buckets.filter = "test1"
expect(getVisibleBuckets(state)).toEqual(["test1", "test11"]) expect(getFilteredBuckets(state)).toEqual(["test1", "test11"])
}) })
}) })

View File

@ -19,7 +19,7 @@ import { createSelector } from "reselect"
const bucketsSelector = state => state.buckets.list const bucketsSelector = state => state.buckets.list
const bucketsFilterSelector = state => state.buckets.filter const bucketsFilterSelector = state => state.buckets.filter
export const getVisibleBuckets = createSelector( export const getFilteredBuckets = createSelector(
bucketsSelector, bucketsSelector,
bucketsFilterSelector, bucketsFilterSelector,
(buckets, filter) => buckets.filter(bucket => bucket.indexOf(filter) > -1), (buckets, filter) => buckets.filter(bucket => bucket.indexOf(filter) > -1),

View File

@ -82,6 +82,11 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.buckets__list {
height: calc(~"100vh - 246px");
overflow: auto;
}
.buckets__item { .buckets__item {
padding: 0.8rem @sidebar-padding 0.8rem @sidebar-padding*2.2; padding: 0.8rem @sidebar-padding 0.8rem @sidebar-padding*2.2;
background: url(../../img/icons/bucket.svg) no-repeat left 2.2rem center; background: url(../../img/icons/bucket.svg) no-repeat left 2.2rem center;