Skip to content

Commit

Permalink
✨ Expose internal methods using ref (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
frinyvonnick authored Jan 6, 2020
1 parent 41c4616 commit 50fb563
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 60 deletions.
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,65 @@ function Example({ items, fetchMore, hasMore }) {
| loadMoreItems | no | function | A function that will be called each time the list need to load more items. |
| placeholder | no | node | Any render-able value like strings or React.Nodes to be displayed while `children` is loading |
| customScrollbar | no | boolean | A boolean that determines if [react-custom-scrollbars](https://github.com/malte-wessel/react-custom-scrollbars) is used instead of native one |
| ref | no | ref or function | A ref or a callback ref to get component instance so you can call instance's methods (see [Methods section](/README.md#methods)) |

## Methods

**scrollTo(scrollOffset: number): void**

see [FixedSizeList](https://react-window.now.sh/#/api/FixedSizeList) methods section.

**scrollToItem(index: number, align: string = "auto"): void**

see [FixedSizeList](https://react-window.now.sh/#/api/FixedSizeList) methods section.

**resetloadMoreItemsCache(): void**

Clear previously loaded items from cache.

example
```jsx
import React from 'react'

import InfiniteLoading from 'react-simple-infinite-loading'

function Example({ items, fetchMore, hasMore }) {
const ref = React.useRef()
const scrollToTop = () => {
if (ref.current) {
ref.current.scrollTo(0)
}
}
const scrollTo50 = () => {
if (ref.current) {
ref.current.scrollToItem(50)
}
}
const resetCache = () => {
if (ref.current) {
ref.current.resetloadMoreItemsCache()
}
}

return (
<>
<button onClick={scrollToTop}>Scroll to top</button>
<button onClick={scrollTo50}>Scroll to 50</button>
<button onClick={resetCache}>Reset cache</button>
<div style={{ width: 300, height: 300 }}>
<InfiniteLoading
hasMoreItems={hasMore}
itemHeight={40}
loadMoreItems={fetchMore}
ref={ref}
>
{items.map(item => <div key={item}>{item}</div>)}
</InfiniteLoading>
</div>
</>
)
}
```

## License

Expand Down
23 changes: 22 additions & 1 deletion example/src/App.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import React, { useState } from 'react'
import React, { useState, useRef } from 'react'
import InfiniteLoading from 'react-simple-infinite-loading'

export default function App() {
const [items, setItems] = useState([...Array(100)].map((_, index) => index))
const ref = useRef()
const scrollToTop = () => {
if (ref.current) {
ref.current.scrollTo(0)
}
}
const scrollTo50 = () => {
if (ref.current) {
ref.current.scrollToItem(50)
}
}
const resetCache = () => {
if (ref.current) {
ref.current.resetloadMoreItemsCache()
}
}

const loadMoreItems = () => {
const newItems = [...Array(100)].map((_, index) => items.length + index)
Expand All @@ -19,11 +35,16 @@ export default function App() {
<div className="app">
<h1>React simple infinite loading example</h1>

<button onClick={scrollToTop}>Scroll to top</button>
<button onClick={scrollTo50}>Scroll to 50</button>
<button onClick={resetCache}>Reset cache</button>
<h2>Start scrolling here :</h2>
<InfiniteLoading
hasMoreItems
itemHeight={40}
loadMoreItems={loadMoreItems}
customScrollbar
ref={ref}
>
{items.map(item => <div className="item" key={item}>{item}</div>)}
</InfiniteLoading>
Expand Down
152 changes: 93 additions & 59 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef } from 'react'
import React, { Component, createRef } from 'react'
import PropTypes from 'prop-types'

import { FixedSizeList as List } from 'react-window'
Expand All @@ -10,60 +10,104 @@ const CustomScrollbarsVirtualList = React.forwardRef((props, ref) => (
<CustomScrollbars {...props} forwardedRef={ref} />
))

const InfiniteLoading = forwardRef((
{
children,
hasMoreItems,
itemsCount,
itemHeight,
loadMoreItems,
placeholder,
customScrollbar
},
ref
) => {
let effectiveCount = itemsCount
if (effectiveCount === undefined) {
effectiveCount = hasMoreItems ? children.length + 1 : children.length
class InfiniteLoading extends Component {
static propTypes = {
hasMoreItems: PropTypes.bool,
loadMoreItems: PropTypes.func,
itemsCount: PropTypes.number,
children: PropTypes.array.isRequired,
itemHeight: PropTypes.number.isRequired,
placeholder: PropTypes.node,
customScrollbar: PropTypes.bool
}

const isItemLoaded = index => !hasMoreItems || index < children.length
constructor(props) {
super(props)
this.infiniteLoaderRef = createRef()
this.fixedSizeListRef = null
}

return (
<AutoSizer>
{({ height, width }) => (
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={effectiveCount}
loadMoreItems={loadMoreItems}
ref={ref}
>
{({ onItemsRendered, ref }) => (
<List
height={height}
itemCount={effectiveCount}
itemSize={itemHeight}
onItemsRendered={onItemsRendered}
ref={ref}
width={width}
outerElementType={customScrollbar ? CustomScrollbarsVirtualList : null}
>
{({ index, style }) => (
<div style={style}>
{children[index] != null ? children[index] : placeholder}
</div>
)}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
)
})
resetloadMoreItemsCache() {
if (this.infiniteLoaderRef.current) {
this.infiniteLoaderRef.current.resetloadMoreItemsCache()
} else {
throw Error('The InfiniteLoader component is not mounted yet')
}
}

scrollTo(...args) {
if (this.fixedSizeListRef) {
this.fixedSizeListRef.scrollTo(...args)
} else {
throw Error('The FixedSizeList component is not mounted yet')
}
}

scrollToItem(...args) {
if (this.fixedSizeListRef) {
this.fixedSizeListRef.scrollToItem(...args)
} else {
throw Error('The FixedSizeList component is not mounted yet')
}
}

render() {
const {
children,
hasMoreItems,
itemsCount,
itemHeight,
loadMoreItems,
placeholder,
customScrollbar
} = this.props

let effectiveCount = itemsCount
if (effectiveCount === undefined) {
effectiveCount = hasMoreItems ? children.length + 1 : children.length
}

const isItemLoaded = index => !hasMoreItems || index < children.length

return (
<AutoSizer>
{({ height, width }) => (
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={effectiveCount}
loadMoreItems={loadMoreItems}
ref={this.infiniteLoaderRef}
>
{({ onItemsRendered, ref: setListRef }) => (
<List
height={height}
itemCount={effectiveCount}
itemSize={itemHeight}
onItemsRendered={onItemsRendered}
ref={listRef => {
this.fixedSizeListRef = listRef
setListRef(listRef)
}}
width={width}
outerElementType={customScrollbar ? CustomScrollbarsVirtualList : null}
>
{({ index, style }) => (
<div style={style}>
{children[index] != null ? children[index] : placeholder}
</div>
)}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
)
}
}

CustomScrollbars.propTypes = {
onScroll: PropTypes.func.isRequired,
forwardedRef: PropTypes.node.isRequired,
forwardedRef: PropTypes.func.isRequired,
style: PropTypes.object.isRequired,
children: PropTypes.node.isRequired
}
Expand All @@ -88,14 +132,4 @@ function CustomScrollbars ({ onScroll, forwardedRef, style, children }) {
)
}

InfiniteLoading.propTypes = {
hasMoreItems: PropTypes.bool,
loadMoreItems: PropTypes.func,
itemsCount: PropTypes.number,
children: PropTypes.array.isRequired,
itemHeight: PropTypes.number.isRequired,
placeholder: PropTypes.node,
customScrollbar: PropTypes.bool
}

export default InfiniteLoading

0 comments on commit 50fb563

Please sign in to comment.