What I Expect
When I am mid scroll, and the user changes the input, the scroll index should jump back to the top of the list once the new data loads.
What is happening
I was able to reset the scroll back to the top, but now I am getting warnings that the list ref and the onItemsRendered is invalid. Even though they are warnings they are hindering the list from populating. I have tested this by taking out the scrollToTop logic and replacing the List ref with the ref provided by InfiniteLoader.
Am I implementing the new ref incorrectly?


The Code
import React from 'react';
import PropTypes from 'prop-types';
import Downshift from 'downshift';
import styled from 'styled-components';
import { FixedSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import MenuItem from '@material-ui/core/MenuItem';
import { withStyles } from '@material-ui/core/styles';
import BaseComponent from '../BaseComponent';
export class SearchFieldInfiniteScrollComponent extends BaseComponent {
listRef = React.createRef();
scrollToTop() {
this.listRef.current.scrollToItem({ align: 'start' });
}
render() {
const {
dataArray,
dropdownWidth,
handleInput,
handleSelection,
isFirstListRender,
isRowLoaded,
itemContent,
listHeight,
loadedRowCount,
loadingIndicator,
loadMoreRows,
placeholder,
rowHeight,
totalCount,
threshold,
} = this.props;
return (
<SearchContainer>
<Downshift
onChange={handleSelection}
stateReducer={stateReducer}
itemToString={item => (item ? item.value : '')}
>
{({ getInputProps, getMenuProps, isOpen, highlightedIndex, getItemProps }) => {
return (
<div>
<Input
{...getInputProps({
onChange: e => {
if (!e.target.value.length) {
this.scrollToTop();
}
return handleInput(e);
},
})}
placeholder={placeholder}
/>
<UnorderedList {...getMenuProps()}>
{isOpen && dataArray && (
<InfiniteLoader
isItemLoaded={isRowLoaded}
loadMoreItems={loadMoreRows}
itemCount={loadedRowCount}
threshold={threshold}
>
{({ onItemsRendered }) => (
<List
ref={this.listRef}
height={listHeight}
onItemsRendered={onItemsRendered}
itemCount={loadedRowCount}
itemSize={rowHeight}
width={dropdownWidth}
>
{({ index, style }) => {
let content = (
<Loading>
{(isFirstListRender || index < totalCount) && loadingIndicator}
</Loading>
);
if (dataArray[index]) {
content = (
<StyledMenuItem
selected={highlightedIndex === index}
component='div'
{...getItemProps({
key: itemContent,
index,
item: dataArray[index],
})}
>
{itemContent(index)}
</StyledMenuItem>
);
}
return (
<RowContainer key={`${index}-${Math.random()}`} style={style}>
{content}
</RowContainer>
);
}}
</List>
)}
</InfiniteLoader>
)}
</UnorderedList>
</div>
);
}}
</Downshift>
</SearchContainer>
);
}
}
export default withStyles(styles, { withTheme: true })(SearchFieldInfiniteScrollComponent);
Please read the docs. The ref property needs to be attached to the inner list, as shown in the docs.

This is what the warning message is talking about.
Fix this first, then if things are still broken- we can talk more.
This is what I based the idea off of..
https://codesandbox.io/s/github/bvaughn/react-window/tree/master/website/sandboxes/scrolling-to-a-list-item
When I use the ref provided from the infiniteLoader I get the following error

For my component to work I need to call to the ref and reset the scroll when the input is emptied. By replacing listRef with the ref of the inner list I continuously receive that error.
Here is the code that I changed
...
export class SearchFieldInfiniteScrollComponent extends BaseComponent {
listRef;
scrollToTop() {
this.listRef.current.scrollToItem({ align: 'start' });
}
render() {
...
<InfiniteLoader
isItemLoaded={isRowLoaded}
loadMoreItems={loadMoreRows}
itemCount={loadedRowCount}
threshold={threshold}
>
{({ onItemsRendered, ref }) => {
this.listRef = ref;
return (
<List
ref={ref}
height={listHeight}
onItemsRendered={onItemsRendered}
itemCount={loadedRowCount}
itemSize={rowHeight}
width={dropdownWidth}
>
I should mention that when I try to access the scrollToItem method with the inner ref within the List component I still get the above error
you should understand which ref is for which component, try <List ref={listRef => { this.listRef = listRef; ref(listRef); }} />
ref is not what you think it is, as @nihgwu mentioned and as the docs I linked to above indicate :) It's a ref (function) you're meant to pass on and attach to the inner list so that infinite loader gets a ref to that list.
{({ onItemsRendered, ref }) => (
<List
ref={list => {
ref(list); // Give InfiniteLoader a reference to the list
this.listRef.current = ref; // Set your own ref to it as well.
}}
/>
)}
I'm going to close this issue, since I'm pretty sure it's been been answered now. If you are still running into any additional problems, it would probably be best provide a complete runnable repro in Code Sandbox. :)
ref={list => { ref(list); // Give InfiniteLoader a reference to the list this.listRef.current = ref; // Set your own ref to it as well. }}
My case:
Instead of the this.listRef.current = ref; i'm wrote this.listRef.current = list;
_InfntLdrHOC.tsx_
import InfiniteLoader from 'react-window-infinite-loader';
...
<InfiniteLoader
isItemLoaded={isItemLoaded}
loadMoreItems={onScroll}
itemCount={listCount}
>
{({ onItemsRendered, ref }) => {
return (
<ListOfMessages
config={{
messages,
height,
width,
ref,
onItemsRendered,
isFetchingNewScenario,
}}
/>
);
}}
</InfiniteLoader>
_ListOfMessages.tsx_
import React from 'react';
import { VariableSizeList as List } from 'react-window';
import Message from '../Message';
class ListOfMessagesClass extends React.Component<any> {
private listRef = React.createRef<any>();
componentDidUpdate(prevProps: any) {
const {
isFetchingNewScenario: isFetchingNewScenarioPrev,
} = prevProps.config;
// eslint-disable-next-line react/destructuring-assignment
const { isFetchingNewScenario } = this.props.config;
if (isFetchingNewScenarioPrev && !isFetchingNewScenario) {
this.scrollToNewMessage();
}
}
scrollToNewMessage = () => {
this.listRef.current!.scrollToItem(0);
};
render() {
const { config } = this.props;
const {
messages = [],
listCount,
height,
width,
ref,
onItemsRendered,
} = config;
const listLength = messages.length;
return (
<>
<List
height={height}
itemCount={listLength}
itemSize={(index: any) => messages[index].messageRenderHeight}
width={width}
ref={list => {
ref(list); // Give InfiniteLoader a reference to the list
// @ts-ignore
this.listRef.current = list; // Set your own ref to it as well.
}}
onItemsRendered={onItemsRendered}
itemData={messages}
outerRef={this.outerRef}
>
{Message}
</List>
</>
);
}
}
export default ListOfMessages;
Most helpful comment
refis not what you think it is, as @nihgwu mentioned and as the docs I linked to above indicate :) It's a ref (function) you're meant to pass on and attach to the inner list so that infinite loader gets a ref to that list.I'm going to close this issue, since I'm pretty sure it's been been answered now. If you are still running into any additional problems, it would probably be best provide a complete runnable repro in Code Sandbox. :)