-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8b7eb63
commit f80a457
Showing
8 changed files
with
2,738 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
repo_token: cWHL7w4ZYPnJJUIxELXwfrKHA18BtMEwU |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
logs | ||
*.log | ||
npm-debug.log* | ||
/index.js | ||
|
||
# Runtime data | ||
pids | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,43 @@ | ||
# react-lazy-list | ||
Lazy list react component for render very large Infinitely lists | ||
This is a simple and fast realization of lazy list as the react component that show entities only when user can see them. Thats why it can render very large lists and does not lose performance. | ||
|
||
```javascript | ||
import React, { Component } from 'react' | ||
import LazyList from 'react-lazy-list' | ||
|
||
export default class App extends Component { | ||
render() { | ||
const onMoreHandle = () => void | ||
return ( | ||
<LazyList elementHeight={ 300 } onMore={ this.onMoreHandle } > | ||
{ entities.map(entity => ( | ||
<Item key={ entity.id } entity={ entity } /> | ||
)) } | ||
<Dummy /> | ||
</LazyList> | ||
) | ||
} | ||
} | ||
``` | ||
|
||
## props | ||
|
||
### `children` | ||
`Object` **required** Entities collection to render. | ||
|
||
### `elementHeight` | ||
`Number` **required** Pixels number of height of collection entity. | ||
|
||
### `windowHeight` | ||
`Number` Custom container height in pixels, i.e. for server side rendering. | ||
|
||
### `topScrollOffset` | ||
`Number` Offset from top of window to increase the threshold of the moment trip. | ||
Usable to make a scroll offset for header height. | ||
|
||
### `bottomScrollOffset` | ||
`Number` Offset from top of window to decrease the threshold of the moment trip. | ||
Usable for increase value an scroll offset for header height. | ||
|
||
### `onMore` | ||
`Function` Handler to call then list seek to end. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"name": "react-lazy-list", | ||
"version": "0.1.0", | ||
"description": "Lazy list react component for render very large Infinitely lists", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "rollup -c rollup.config.js", | ||
"test": "npm run build && jest", | ||
"coverage": "npm test -- --coverage", | ||
"coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls", | ||
"prepare": "npm run build" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/MaxSvargal/react-lazy-list.git" | ||
}, | ||
"keywords": [ | ||
"react", | ||
"lazy", | ||
"list", | ||
"scroll", | ||
"infinite", | ||
"fast", | ||
"render" | ||
], | ||
"author": "MaxSvargal", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/MaxSvargal/react-lazy-list/issues" | ||
}, | ||
"homepage": "https://github.com/MaxSvargal/react-lazy-list#readme", | ||
"peerDependencies": { | ||
"react": ">=15" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^2.11.16", | ||
"enzyme": "^2.7.1", | ||
"jest": "^18.1.0", | ||
"react": "^15.4.2", | ||
"react-addons-test-utils": "^15.4.2", | ||
"react-dom": "^15.4.2", | ||
"rollup": "^0.41.4", | ||
"rollup-plugin-buble": "^0.15.0" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import buble from 'rollup-plugin-buble' | ||
|
||
export default { | ||
entry: 'src/index.js', | ||
dest: 'index.js', | ||
plugins: [ buble() ], | ||
format: 'cjs', | ||
external: [ 'react' ] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import React, { Component, PropTypes } from 'react' | ||
|
||
export default class LazyList extends Component { | ||
constructor(props) { | ||
super(props) | ||
this.scrollListener = this.scrollListener.bind(this) | ||
|
||
const { windowHeight, elementHeight, children } = props | ||
const computedWindowHeight = windowHeight || document && document.body.clientHeight | ||
const computedElementHeight = elementHeight | ||
const showNum = Math.round(computedWindowHeight / computedElementHeight) | ||
const visibleChildren = children.slice(0, showNum) | ||
|
||
this.state = { | ||
showNum, | ||
visibleChildren, | ||
scrolledNum: 0, | ||
topOffset: 0, | ||
bottomOffset: 0 | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
window.addEventListener('scroll', this.scrollListener) | ||
} | ||
|
||
componentWillUnmount() { | ||
window.removeEventListener('scroll', this.scrollListener) | ||
} | ||
|
||
componentWillReceiveProps(props) { | ||
const { scrolledNum, showNum } = this.state | ||
const { children } = this.props | ||
|
||
children.length !== props.children.length && | ||
this.setState({ visibleChildren: props.children.slice(scrolledNum, scrolledNum + showNum + 1) }) | ||
} | ||
|
||
shouldComponentUpdate(props, state) { | ||
return this.state.topOffset !== state.topOffset || | ||
this.props.children.length !== props.children.length | ||
} | ||
|
||
scrollListener() { | ||
const { showNum } = this.state | ||
const { | ||
children, elementHeight, topScrollOffset, | ||
onLoad, windowHeight, bottomScrollOffset | ||
} = this.props | ||
|
||
const fullHeight = children.length * elementHeight | ||
const scrolled = (window && window.scrollY || 0) - topScrollOffset | ||
const scrolledNum = Math.round((scrolled > 0 ? scrolled : 0) / elementHeight) - 1 | ||
|
||
const visibleChildren = children.slice(scrolledNum, scrolledNum + showNum + 1) | ||
const topOffset = scrolledNum * elementHeight | ||
const bottomOffset = fullHeight - (topOffset + (showNum * elementHeight)) | ||
const breakPoint = fullHeight - windowHeight - bottomScrollOffset | ||
|
||
if (this.state.topOffset !== topOffset || this.state.bottomOffset !== bottomOffset) { | ||
this.setState({ topOffset, bottomOffset, scrolledNum, visibleChildren }) | ||
scrolled >= breakPoint && onLoad() | ||
} | ||
} | ||
|
||
render() { | ||
const { visibleChildren, topOffset, bottomOffset } = this.state | ||
return ( | ||
<div> | ||
<div style={ { marginTop: topOffset } } /> | ||
<div style={ { position: 'relative', width: '100%' } }>{ visibleChildren }</div> | ||
<div style={ { marginBottom: bottomOffset } } /> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
LazyList.propTypes = { | ||
children: PropTypes.array.isRequired, | ||
elementHeight: PropTypes.number.isRequired, | ||
windowHeight: PropTypes.number, | ||
topScrollOffset: PropTypes.number, | ||
bottomScrollOffset: PropTypes.number, | ||
onLoad: PropTypes.func | ||
} | ||
|
||
LazyList.defaultProps = { | ||
topScrollOffset: 0, | ||
bottomScrollOffset: 0 | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const React = require('react') | ||
const { shallow, mount } = require('enzyme') | ||
const ReactTestUtils = require('react-addons-test-utils') | ||
const LazyList = require('./index') | ||
|
||
describe('LazyList', () => { | ||
let lazyListElement | ||
const children = [...Array(4)].map((v, key) => React.createElement('li', { key })) | ||
const props = { elementHeight: 100, windowHeight: 250, onLoad: jest.fn() } | ||
|
||
beforeEach(() => { | ||
lazyListElement = React.createElement(LazyList, props, children) | ||
}) | ||
|
||
test('should render initial number of visible children', () => { | ||
const wrapper = shallow(lazyListElement) | ||
expect(wrapper.find('li').length).toBe(3) | ||
}) | ||
|
||
test('should render without windowHeight', () => { | ||
Object.defineProperty(document.body, 'clientHeight', { value: 300 }) | ||
lazyListElement = React.createElement(LazyList, { elementHeight: 100 }, children) | ||
const wrapper = mount(lazyListElement) | ||
expect(wrapper.find('li').length).toBe(3) | ||
}) | ||
|
||
test('should rerender when pass a new children', () => { | ||
const wrapper = shallow(lazyListElement) | ||
expect(wrapper.find('li').length).toBe(3) | ||
|
||
wrapper.setProps({ | ||
elementHeight: 100, | ||
children: Array.prototype.concat(children, React.createElement('li', { key: 4 })) | ||
}) | ||
wrapper.render() | ||
expect(wrapper.find('li').length).toBe(4) | ||
}) | ||
|
||
test('should show next entities on window scroll and call onLoad callback', () => { | ||
window.addEventListener = function(event, handler) { | ||
window.scrollY = 150 | ||
handler() | ||
} | ||
const wrapper = mount(lazyListElement) | ||
const els = wrapper.find('li') | ||
|
||
expect(els.length).toBe(3) | ||
expect(els.at(0).key()).toBe('1') | ||
expect(els.at(1).key()).toBe('2') | ||
expect(els.at(2).key()).toBe('3') | ||
expect(props.onLoad.mock.calls.length).toBe(1) | ||
}) | ||
|
||
test('should unsubscribe from scroll listener on unmount', () => { | ||
window.removeEventListener = jest.fn() | ||
const mockCalls = window.removeEventListener.mock.calls | ||
const wrapper = mount(lazyListElement) | ||
|
||
expect(mockCalls.length).toBe(0) | ||
wrapper.unmount() | ||
expect(mockCalls.length).toBe(1) | ||
}) | ||
}) |
Oops, something went wrong.