I am using the List and WindowScroller in react-virtualized to display logs. It seems that there is no good way to implement the auto-follow. It seems pretty easy to scroll to the bottom with scrollToIndex. But it is pretty hard to know whether we are at the bottom of the page. the document.body.clientHeight is much smaller than the scrollTop provided by WindowScroller. How could we implement this feature? Thanks.
you can use the prop "scrollToIndex" when his value is equal to your row count
render(): React.Element<AutoSizer> {
const { data, ...otherProps } = this.props;
return (
<AutoSizer onResize={this.onResizeHandler}>
{({ width, height }) => (
<List
{...otherProps}
deferredMeasurementCache={this.cache}
rowHeight={this.cache.rowHeight}
rowCount={data.length}
forceUpdateGrid={data}
rowRenderer={this.rowRenderer}
scrollToIndex={data.length - 1}
onScroll={this.onScrollHandler}
height={height}
width={width}
/>
)}
</AutoSizer>
);
}
Related
I'm using a react-virtualized List component, which uses height data from CellMeasurerCache:
const cache = new CellMeasurerCache({fixedWidth: true, rowHeight: 150, minHeight: 50 });
This is the code for the list:
return (
<div className={classes.maxHeight}>
<AutoSizer>
{({height, width}) =>
<List
className='List'
height={height}
width={width}
overscanRowCount={5}
rowCount={content.length}
rowHeight={cache.rowHeight}
rowRenderer={listRowRenderer(content)}>
</List>
}
</AutoSizer>
</div>);
The problem is that, sometimes the height is defaulting to minHeight, and i want it to use the actual computed height
See this image , the two cards have a default height of 50, when they should have a computed height based on their content. If I scroll down and back up, they will be computed properly, so should this be forced somehow or...?
The listRowRenderer:
const item = data[index];
return (
<CellMeasurer
cache={cache}
columnIndex={0}
key={key}
rowIndex={index}
parent={parent}>
{({measure, registerChild}) =>
<div ref={registerChild} style={style}>
(content here)
</div>
}
</CellMeasurer>
);
I even tried using the measure from CellMeasurer inside useEffect of the card component
Turns out it's not okay to use 1 cache in multiple locations. I had an UI with 3 tabs, each tab loading virtualized data, and each tab used the same cache which messed things up. I gave each tab it's individual cache and voila it worked.
I'd like to virtualize a large list in my react application and use react-virtualized-auto-sizer and react-window-infinite-loader packages. Below is how I did it.
import { FixedSizeList as List } from 'react-window'
import InfiniteLoader from 'react-window-infinite-loader'
import AutoSizer from 'react-virtualized-auto-sizer'
<AutoSizer>
{({ height, width }) => (
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={10}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
outerElementType="section"
innerElementType="ol"
height={height}
itemCount={10}
itemSize={75}
overscanCount={4}
onItemsRendered={onItemsRendered}
ref={ref}
width={width}
>
{Row}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
But it displays nothing on the screen. If I remove the usage of AutoSizer, then it works as expected.
You can check the behavior here: https://codesandbox.io/s/react-window-ep2rz3?file=/src/App.js:524-1261
I really appreciate any help!!
Sorry I will make a better answer here :
I think it's because the parent of Autosizer needs an height make it work.
See the docs: https://github.com/bvaughn/react-virtualized/blob/master/docs/usingAutoSizer.md#why-is-my-autosizer-setting-a-height-of-0
If you force your .App container to a fixed height, you will see your list
I am currently using react-virtualized InfiniteLoader component to scroll over a List of elements that could be variable all the time.
This is my component now:
<VirtualizedInfiniteLoader isRowLoaded={isRowLoaded} loadMoreRows={loadMoreRows} rowCount={rowCount}>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ width, height }) => (
<List
key={`${width}${height}${rowCount}`}
height={height - headerHeight}
onRowsRendered={onRowsRendered}
scrollToIndex={scrollToIndex}
scrollToAlignment={scrollToAlignment}
ref={registerChild}
rowCount={rowCount}
rowHeight={rowHeight}
rowRenderer={rowRenderer}
width={width}
onScroll={onListScroll}
/>
)}
</AutoSizer>
)}
</VirtualizedInfiniteLoader>
And i'm using this in a container:
renderInfiniteLoader = currentStatus => {
const { cache, headerHeight } = this.state;
const { scrollToIndex, params, allTasks, selectedFilterFields, searchFilters } = this.props;
const list = allTasks[currentStatus];
console.log(list.length, params);
return (
<InfiniteLoaderWrapper
diffHeight={marginBottomToAdd({ searchFilters, selectedFilterFields, customerID: params.customerID })}
ref={this.cardsContainer}
>
<InfiniteLoader
loadMoreRows={options => this.loadMoreRows(options, status)}
rowRenderer={this.renderTasks(list, currentStatus)}
isRowLoaded={this.isRowLoaded(list)}
scrollToIndex={scrollToIndex}
scrollToAlignment="start"
rowCount={list.length + 1}
headerHeight={150}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
onListScroll={this.onInfiniteLoaderScroll}
/>
</InfiniteLoaderWrapper>
);
};
The problem here is that everytime i scroll down and a loader appear if i'm scrolling too fast, page scroll to first element on top.
The prop scrollToIndex seems to be 0 everytime so probably that's the problem.
If i remove that prop, scrolling works correctly, but comes up another issue:
I can click on list's element to go to a detail page, and if i come back i expect to be in the correct position of the list, but i'm on top (again, because scrollToIndex is 0).
What is the correct value to pass to scrollToIndex to be always in the right point?
I can see there was this bug in some version of react-virtualized but can't find any workaround.
Thanks in advance!
I am trying to get my react-virtualized List component to work together with ArrowKeyStepper without success. I must be missing something but could not find any examples of this implementation. There are no errors it just does not function as expected.
So far my code looks like this:
import { List as VirtualizedList, AutoSizer, ArrowKeyStepper } from "react-virtualized";
...skip a lot of other stuff
<div data-testid="popover-items">
<ArrowKeyStepper columnCount={1} rowCount={numFilteredOptions} mode="cells">
{({ onSectionRendered, scrollToRow, scrollToColumn }) => (
<AutoSizer disableHeight={!fullScreen}>
{({ width, height }) => (
<VirtualizedList
rowHeight={rowHeight}
height={fullScreen ? height - 175 : VIRTUAL_LIST_HEIGHT}
onRowsRendered={onSectionRendered}
columnCount={1}
rowCount={numFilteredOptions}
rowRenderer={renderRow}
noRowsRenderer={() => null}
scrollToRow={scrollToRow}
scrollToColumn={scrollToColumn}
width={width}
/>
)}
</AutoSizer>
)}
</ArrowKeyStepper>
</div>
Expected Behavior: arrow up/down would move the selection of an item in the list.
Actual Behavior: nothing happens.
I have a fixed list and grid using react-window, infinite loader, and auto sizer. It's working fine but I'm looking to append a component before the list/grid starts (for example, a search box and a button). What's the correct approach to accomplish this? I want this component to scroll with the fixed list and not scroll separately. I've tired just rendering the component before but then it's not in the fixed list container and doesn't scroll with it.
{/* WANT TO ADD SOME SORT OF COMPONENT HERE SO IT CAN SCROLL WITH LIST */}
{/* CAN'T IN HERE BECAUSE ITS NOT INSIDE THE FIXED CONTAINER SO IT SCROllS SEPARATELY */}
<AutoSizer>
{({ height, width }) => (
<InfiniteLoader
isItemLoaded={index => index < stateData.data.length}
itemCount={stateData.data.length + 1}
loadMoreItems={loadMoreProducts}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
onItemsRendered={onItemsRendered}
ref={ref}
height={height}
itemCount={stateData.data.length + 1}
itemSize={350}
width={width}
>
{/* WANT TO ADD SOME SORT OF COMPONENT HERE SO IT CAN SCROLL WITH LIST */}
{/* CAN'T IN HERE BECAUSE ITS LOOPING LISTITEM */}
{ListItem}
</FixedSizeList>
)}
</InfiniteLoader>
)}
</AutoSizer>
Edit: Pretty much I don't want the list (or grid) and the actual page to be two different scroll containers. I'd like the whole page to scroll together. I've come across this issue because some of my containers need to have an infinite list of items users can scroll through so the list needed to be virtualized to improve performance.
See a demo here. Really the fixed container should just be considered the whole page and the search box and everything else should scroll with the infinite list. Having two different scroll containers isn't too great for the ux.
If you want the whole page to scroll with your virtual list you will need a window scroller. This also involves playing around with some styling as well. To make your searchbox scroll with your list you will need a position of fixed. A spacer div and some styling helps the illusion.
I added a basic window scroller and some styling changes to your codepen here.
A few options:
Make a wrapper around The autosizer, that holds both your search box and the autosizer.
<>
<SearchBox />
<AutoSizer>
... your items
</AutoSizer>
</>
let the AutoSizer contain both the search and the list
<AutoSizer>
<SearchBox />
<List>
... your items
</List>
</AutoSizer>
Since your infinite scroll is using a render function you might need fragments
<AutoSizer>
<InfiniteScroll>
{({ onItemsRendered, ref }) => (
<>
<SearchBox />
<List>
... your items
</List>
<>
)}
</InfiniteScroll>
</AutoSizer>
Here I edited your example and made it work using this approach.
Please note that the page scrolls only because of the top and bottom bars; which is the behaviour in your example.
Let the infinite loader populate an array, and append an extra item to that. The item can have a certain type. Assuming the FixedSizeList is the List component from react-virtualized then you can use it's rowRenderer to render the item of type "search" differently than the other items. Something like:
function rowRenderer ({ key, type, value, ...rest }) {
if (type === 'search') {
return <Search key={key} placeholder={value} {...rest} />
}
return <div key={key} {...rest}>{value}</div>
}
// Render your list
ReactDOM.render(
<AutoSizer>
{({ height, width }) => (
<List
height={height}
rowCount={list.length}
rowHeight={20}
rowRenderer={rowRenderer}
width={width}
/>
)}
</AutoSizer>,
document.getElementById('example')
);
Perhaps it also helps to look at the simple example from Vaughn.