I have a component structured as below. It uses react-waypoint to fetch and render additional cards once a user reaches the end of the card collection.
componentDidMount() {
const { actions, user } = this.props;
if (!user) return;
actions.getConnections(user.guid, 0, DEFAULT_FETCH_ROW_COUNT)
.then(({ data }) => {
this.setState({
fullyLoaded: data.length < DEFAULT_FETCH_ROW_COUNT,
firstLoaded: true
});
});
}
loadMoreConnections(rows = DEFAULT_FETCH_ROW_COUNT) {
const { user, connections, actions } = this.props;
const { fullyLoaded } = this.state;
if (fullyLoaded) return;
return actions.getConnections(user.guid, connections.length, rows, true)
.then(({ data }) => {
this.setState({
fullyLoaded: data.length < rows
});
});
}
showConnections() {
const { user, connections, actions } = this.props;
return (
connections.map((connection, index) => (
<div className="col-12 col-md-6 col-lg-4 col-xl-3 card-wrapper" key={index}>
{(!this.state.fullyLoaded && index === (connections.length - THRESHOLD)) && (index >= DEFAULT_FETCH_ROW_COUNT - THRESHOLD) && (
<Waypoint
onEnter={() => {
this.loadMoreConnections();
}}
/>
)}
<Link
id={`${idPrefix}ConnectionCardLink${index}`}
to={`/${ROUTES.PROFILE}/${getProfileUrl(connection)}`}
>
<ConnectionCard
idPrefix={idPrefix}
src={connection.photo ? connection.photo.url : ''}
idSuffix={index}
>
<h5 id={`${idPrefix}NameTxt${index}`}
className="text-truncate font-weight-bold">{connection.firstname} {connection.lastname}</h5>
<h6 id={`${idPrefix}PrimaryJobTxt${index}`}
className="text-truncate text-secondary primary-job">
{connection.primaryWorkHistory
? (
`${connection.primaryWorkHistory.jobs[0].jobName} @ ${connection.primaryWorkHistory.employer.name}`
+ `${connection.primaryWorkHistory.employer.location
? `, ${connection.primaryWorkHistory.employer.location.city}, ${connection.primaryWorkHistory.employer.location.state}`
: '' }`
)
: ' '
}
</h6>
<div className="button-group">
<Link
id={`${idPrefix}ViewProfileBtn${index}`}
className="button hollow small"
to={`/${ROUTES.PROFILE}/${getProfileUrl(connection)}`}
>
View Profile
</Link>
<Button
id={`${idPrefix}MessageBtn${index}`}
className="small"
label="Message"
onClick={(e) => {
e.preventDefault();
actions.openChatByParticipants(user, connection);
}}
/>
</div>
</ConnectionCard>
</Link>
</div>
))
);
}
showNoResults() {
return (
<EmptySearchResults
idPrefix={`${idPrefix}EmptySearch`}
title="Don’t be shy."
description="It looks like you don’t have any connections, but don’t worry you can search our community."
>
<Link
id={`${idPrefix}GetConnectedBtn`}
className="button action-button"
to={`/${ROUTES.COMMUNITY}/suggested`}
>
Get Connected
</Link>
</EmptySearchResults>
)
}
render() {
const { connections, isFetchingConnections } = this.props;
const { firstLoaded } = this.state;
let view = null;
if (connections.length) {
view = this.showConnections();
} else if (firstLoaded) {
view = this.showNoResults();
}
return (
<div className="row">
{view}
</div>
);
}
This works as expected, but I want to display a loader component while this additional data is fetched and rendered. To do this, I've tried to implement a loader component by adding/modifying the following:
showLoader() {
return (
<div className="select-overlay-loader d-flex justify-content-center align-items-center">
<Loader />
</div>
);
}
if(isFetchingConnections) {
view = this.showLoader();
} else if (connections.length) {
view = this.showConnections();
} else if (firstLoaded) {
view = this.showNoResults();
}
These changes do display a loader component while there is more card data being fetched and displayed, but after the new cards are appended to the list, the scrollview of the list resets to the top. I am not sure how I can preserve the scroll location of the list in-between fetches.
I have seen answers to similar questions such as this one
that suggest to set the list's scrollTop to the list's current scrollHeght before fetching new data.
I have tried, but have not been able to implement this fix.
Is the issue where the component is placed? I have seen examples where the is placed at the end of a rendered list, but in this case, a component is included at the top of every card Component in the map function.
I am working on an existing codebase and am new to React, so please forgive any cluelessness on my part. I hope my question is presented clearly.
Can anyone point me in the right direction?
Cheers!
In order to implement a reverse infinite-scroll in React.js you need to do something like this:
componentWillUpdate() {
this.scrollHeight = this.scrollableElement.scrollHeight;
this.scrollTop = this.scrollableElement.scrollTop;
}
componentDidUpdate() {
this.scrollableElement.scrollTop = this.scrollTop +
(this.scrollableElement.scrollHeight - this.scrollHeight);
}
Before each update you store the last scroll position, and after updating you just restore it. It did work for me. In my case a waypoint component is placed at the top.
I've found this solution here: http://blog.vjeux.com/2013/javascript/scroll-position-with-react.html.