Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Issues updating InfiniteLoader when collection changes.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { fetchAccounts, searchAccounts, getAccountsSuccess } from '../actions/accountsActions.js';
import AccountsItem from '../components/accounts/AccountsItem';
import AlphaFilter from '../components/ui/AlphaFilter';
import '../styles/accounts.scss';
import { InfiniteLoader, List, AutoSizer } from 'react-virtualized';
import Loading from '../components/ui/Loading';
import BaseTextInput from '../components/ui/BaseTextInput';
export class Accounts extends Component {
constructor(props, context) {
super(props, context);
this.handleActionClick = this.handleActionClick.bind(this);
this._rowRenderer = this._rowRenderer.bind(this);
this.loadingNextPage = false;
this.loadNextPage = this.loadNextPage.bind(this);
this.loadMoreRows = this.loadMoreRows.bind(this);
this.searchChange = this.searchChange.bind(this);
this.searchTimeout = 500; // milliseconds
this.minLetterCount = 3;
this.timeout = -1;
}
componentWillMount() {
fetchAccounts(this.props.appState.authState.user.id).then(
result => {
this.props.dispatch(getAccountsSuccess(result.payload, result.metaData));
}
).catch(
err => {
console.log('could not load accounts ', err);
}
);
}
_rowRenderer ({ index, isScrolling, key, style }) {
let item = this.props.appState.accountsState.accounts[index];
// note: using key here is what caused rows not to render when the collection changed
// use item.id instead because it is unique and causes a render to trigger
return (
<div
key={item.id}
style={style}
>
<AccountsItem itemData={item} className="" />
</div>
);
}
_noRowsRenderer () {
return (
<div className="nostyle">
No rows
</div>
);
}
loadNextPage () {
this.loadingNextPage = true;
return fetchAccounts(this.props.appState.authState.user.id, this.props.appState.accountsState.accountsMetaData.number + 1).then(
result => {
this.props.dispatch(getAccountsSuccess(result.payload, result.metaData));
this.loadingNextPage = false;
}
).catch(
err => {
console.log('could not load accounts ', err);
}
);
}
// Only load 1 page of items at a time.
// Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
loadMoreRows () {
return this.loadingNextPage ? () => {} : this.loadNextPage();
}
// search bindings
clearFilter() {
this._searchInput.clear();
this.filterBySearch = '';
this.forceUpdate();
}
searchChange(name, field) {
let search = field.getValue().toLowerCase();
if(this.timeout > -1) {
window.clearTimeout(this.timeout);
this.timeout = -1;
}
if(search.length >= this.minLetterCount) {
this.timeout = window.setTimeout(()=>{
searchAccounts(this.props.appState.authState.user.id, search).then(
result => {
this.list.forceUpdateGrid();
this.props.dispatch(getAccountsSuccess(result.payload, result.metaData));
}
).catch( message => {
console.log('can not search for accounts: ', message);
});
}, this.searchTimeout);
} else {
console.log('invalid search length ', this.searchTimeout);
}
}
// Every row is loaded except for our loading indicator row.
render() {
let accounts = this.props.appState.accountsState.accounts;
if(accounts == undefined || accounts.length <= 0 ) {
return (<Loading><h5>Refreshing your accounts...</h5></Loading>);
}
let metaData = this.props.appState.accountsState.accountsMetaData;
let currentPage = parseInt(metaData.number);
let totalPages = parseInt(metaData.totalPages );
let hasNextPage = currentPage < totalPages - 1;
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const rowCount = hasNextPage
? accounts.length + 1
: accounts.length;
const isRowLoaded = ({ index }) => !hasNextPage || index < accounts.length || this.search == 'default';
return (
<div className="fullScreen AccountsContainer">
<div className="searchBoxContainer">
<div className="searchBox">
<BaseTextInput
name="search"
type="text"
placeholder="Search by Account name"
className="searchInput"
onChange={this.searchChange}
ref={(c) => this._searchInput = c} />
<span className="removeFilter" onClick={() => {this.clearFilter('');}}><span className="icon"/></span>
</div>
</div>
<div className="Accounts componentContainer">
<div className="infiniteList component">
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={this.loadMoreRows}
rowCount={rowCount}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref="List"
className="fuckthis"
overscanRowCount={0}
noRowsRenderer={this._noRowsRenderer}
rowCount={accounts.length}
rowHeight={60}
rowRenderer={this._rowRenderer}
onRowsRendered={onRowsRendered}
width={width}
height={height}
ref={c => this.list = c}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
</div>
</div>
</div>
);
}
}
Accounts.propTypes = {
appState: PropTypes.object.isRequired,
onNavToggle: PropTypes.func.isRequired,
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
appState: state
};
}
export default connect(
mapStateToProps
)(Accounts);
@jarbot
Copy link
Author

jarbot commented Jan 11, 2017

Using the key from _rowRenderer ({ index, isScrolling, key, style }) is what caused rows not to render when the collection changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment