react-virtulized list with ArrowKeyStepper not working - reactjs

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.

Related

bvaughn / react-virtualized : Stop AutoSizer from keep updating the width when browser window resized dynamically

How do I stop AutoSizer from keep updating the width when browser window resized dynamically ( dragged by mouse etc )?
I just need the width to calculate once ( on first load ) or when page is refreshed, no more recalculation on screen resize after that
How do I do that?
import {
List,
AutoSizer,
CellMeasurer,
CellMeasurerCache,
} from "react-virtualized";
function Result(props) {
...
return (
<AutoSizer disableHeight>
{({ width }) => {
containerWidth = width;
// Get Justified Layout Geometry
renderJustifiedLayoutGeometry();
return (
<React.Fragment
key={Math.random()
.toString(36)
.substr(2, 9)}
>
<div className="c-justified-grid" style={{ width: width }}>
<List
width={width}
height={100}
autoHeight={true}
rowHeight={rowHeight}
rowRenderer={renderRow}
rowCount={new_LayoutGeometry.length}
style={{ contain: "layout" }}
overscanRowCount={3}
key={Math.random()
.toString(36)
.substr(2, 9)}
/>
</div>
</React.Fragment>
);
}}
</AutoSizer>
);
}
As I saw in the comments, I first recommends you to see other options as your description seems a bit sketchy and does not make sense. However, if you really like to do that, you can change the code using the following trick, this way, you will only the first width that the component gives you.
Here is how your code should look like:
import {
List,
AutoSizer,
CellMeasurer,
CellMeasurerCache,
} from "react-virtualized";
function Result(props) {
....
const [initialWidth, setInitialWidth] = useState(-1);
return (
<AutoSizer disableHeight>
{({ width }) => {
if(initialWidth === -1){
setInitialWidth(width);
}
containerWidth = initialWidth;
// Get Justified Layout Geometry
renderJustifiedLayoutGeometry();
return (
<React.Fragment key={ Math.random().toString(36).substr(2, 9) }>
<div className="c-justified-grid" style={{ width: initialWidth }}>
<List
width={initialWidth}
height={100}
autoHeight={true}
rowHeight={rowHeight}
rowRenderer={renderRow}
rowCount={new_LayoutGeometry.length}
style={{ contain:'layout' }}
overscanRowCount={3}
key={ Math.random().toString(36).substr(2, 9) }
/>
</div>
</React.Fragment>
)
}}
</AutoSizer>
);
}
I changed a sample code on codesandbox to give you a live example as well, please feel free to have a look and ask question if needed:
https://codesandbox.io/s/react-virtualized-infiniteloader-forked-j6en7p?file=/src/App.js
Hopefully that helps you with what you wanted to achieve.

React virtualized doesn't work with auto sizer and infinite loader

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

React virtualized infiniteLoader scrolling to top on load using 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!

Append components to a fixed list using react-window?

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.

How to know implement auto follow with react-virtualized

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>
);
}

Resources