Skip to content

Commit

Permalink
Merge pull request #1407 from cozy/load-more
Browse files Browse the repository at this point in the history
feat: Load more component
  • Loading branch information
y-lohse authored Mar 13, 2020
2 parents a315c79 + 91f79d4 commit 55b5b6a
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ module.exports = {
'../react/Figure/FigureBlock.jsx',
'../react/Table/index.jsx',
'../react/UnorderedList/index.jsx',
'../react/OrderedList/index.jsx'
'../react/OrderedList/index.jsx',
'../react/LoadMore/index.jsx'
]
},
{
Expand Down
37 changes: 37 additions & 0 deletions react/LoadMore/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#### Load More

The Load More component can be used at the bottom of infinite lists where it will automatically start loading the next batch of data when it scrolls into view.

```
import LoadMore from 'cozy-ui/transpiled/react/LoadMore';
initialState = { elements: [1, 2] };
const sleep = (timeout) => new Promise(resolve => setTimeout(resolve, timeout))
const fetchMore = async () => {
await sleep(1000);
setState(state => ({ elements: [...state.elements, state.elements.length + 1]}));
}
<div style={{ height: '300px', overflow: 'auto' }}>
<p>
Scroll down to load more.
</p>
{state.elements.map((el, index) => (
<div style={{ background: 'grey', height: '160px', marginBottom: '10px' }} key={index} />
))}
{ state.elements.length < 25 && <LoadMore label="Load more" fetchMore={fetchMore} /> }
</div>
```

If for some reason the loading doesn't happen automatically, the component displays a button that can be used to manually load the next batch of data.
This next example loads nothing, it's just there to show what the button looks like.

```
import LoadMore from 'cozy-ui/transpiled/react/LoadMore';
<div>
<LoadMore label="Load more" fetchMore={() => {}} />
</div>
```
51 changes: 51 additions & 0 deletions react/LoadMore/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState, useEffect, useRef, useCallback } from 'react'
import PropTypes from 'prop-types'
import Button from '../Button'
import Spinner from '../Spinner'

const LoadMore = ({ fetchMore, label }) => {
const [isLoading, setIsLoading] = useState(false)
const elementRef = useRef()

const startFetchMore = useCallback(async () => {
if (!isLoading) {
setIsLoading(true)
await fetchMore()
setIsLoading(false)
}
}, [isLoading, fetchMore])

const checkIntersectionsEntries = intersectionEntries => {
if (intersectionEntries.filter(entry => entry.isIntersecting).length > 0)
startFetchMore()
}

useEffect(() => {
const observer = new IntersectionObserver(checkIntersectionsEntries)
observer.observe(elementRef.current)

return () => {
observer.unobserve(elementRef.current)
observer.disconnect()
}
})

return (
<span ref={elementRef}>
<Button
theme="text"
onClick={startFetchMore}
label={isLoading ? <Spinner noMargin /> : label}
/>
</span>
)
}

LoadMore.propTypes = {
/** A function that is called when the next batch of data needs to be loaded. Can return a promise. */
fetchMore: PropTypes.func.isRequired,
/** The label for the button */
label: PropTypes.string.isRequired
}

export default LoadMore

0 comments on commit 55b5b6a

Please sign in to comment.