bvaughn / react-window Goto Github PK
View Code? Open in Web Editor NEWReact components for efficiently rendering large lists and tabular data
Home Page: https://react-window.now.sh/
License: MIT License
React components for efficiently rendering large lists and tabular data
Home Page: https://react-window.now.sh/
License: MIT License
If scrollbars are visible by default in OS horizontal scrollbar appears in vertical FixedSizeList at least in Chrome browser
How to reproduce
Set show scrollbars always
open
https://react-window.now.sh/#/examples/list/fixed-size
scroll a little bit down and you will see a horizontal scrollbar (scroll overflow is on scrollbar width)
It‘s a amazing work! but what is the difference with react-virtualized? when is it better to choose react-window than react-virtualized?
Similar to #22. Except this time it's for VariableSizeGrid. I've created a few examples to demonstrate.
Horizontal scrollbar appears on OSes with always visible scrollbars (e.g. Windows) once scrolled a bit:
https://codesandbox.io/s/r55m5n027n
The same issue could be reproduced on other OSes (e.g. macOS) when using custom scrollbars via :-webkit-scrollbar
:
https://codesandbox.io/s/618026v83r
The only workaround I've found is to offset each column by scrollbar width:
https://codesandbox.io/s/l2rqwyv989
Ofc then we have to know the scrollbar width beforehand either using a constant width when we have custom scrollbars or using some kind of width detection mechanism (calculating from elm.clientWidth - elm.offsetWidth
). The latter sounds more ugly and could lead to jank when called to frequently. But then again, we would only need to calculate scrollbar width once in user's session.
I'm writing a file listing widget using FixedSizeList
, and after changing to a different folder, I want to scroll back to the top. This sometimes causes the bottom half of all items to disappear, see this codesandbox:
https://8zm2lzr10j.codesandbox.io/
I noticed that in the broken state, there is a negative scrollOffset
. That's about as far as I got with debugging.
Tested with react-window
1.1.1 on Chrome 68.0.3440.106 on Linux, Firefox 60.1.0esr (64-bit) on Linux.
This snippet below stops rendering items if I scroll to fast. Not sure why, The list I have right now while in dev is about 1600 elements. However the real list will be at the minimum 400k elements. There are no errors in the console that can point to any reason why this might be happening.
<div>
<h2>
Results(
{listItems.length})
</h2>
<List
itemCount={listItems.length}
itemSize={50}
height={this.state.height}
width={400}
>
{({ index, style }) => (
<div
style={style}
className={css(styles.searchItem)}
style={styles}
>
<p>{listItems[index].properties.id}</p>
<p>Score: {listItems[index].properties.score}</p>
</div>
)}
</List>
</div>
Hi,
I am just curious how is this different from react-virtualized?
Are there any conceptual differences?
Thanks!
Curious if there is a way to hook into react-window to allow use of position sticky? Similar to https://github.com/marchaos/react-virtualized-sticky-tree. As a quick test I tried rendering a larger row with sticky headers, but it doesn't seem to be working. Seems like we would need some hooks or a way to tell the header to stay in view for so many pixels 🤔. Still working on things and will report back with whatever I find, just wanted to see if this has been thought about or explored yet.
Calling scrollTo
on a VariableSizeGrid
component seems to cause the page to crash, as seen in the following example:
https://codesandbox.io/s/ypq502woz9
I did notice, however, that there are no test cases for scrollTo
on VariableSizeGrid
components, so apologies if the two aren't meant to work together!
For example, on the example of the Fixed Size List, items in the row are labelled "Item n" in the demo, but Row {index}
in the code sample.
I wanted to submit a PR to fix this, but since code examples are reused (e.g. FixedSizeList in both Fixed Size List as Scrolling to an item), I couldn't simply modify them, and since the horizontal and vertical versions of the Fixed Size List both reuse the code that labels them with "Item", I could change the demo elements either. (I also didn't manage to quickly get it to run locally by following the instructions in website/README either, btw...)
Everything works fine when rowHeight
or columnWidth
selectors never change, but when they do change, rows and columns that were already rendered do not get their styles updated.
I've created an example sandbox with AutoSizer for this issue:
https://codesandbox.io/s/2p1jrywm30
When width of the element changes, e.g. user changing width of the window, AutoSizer
updates the width and height, but VariableSizeGrid
does not refresh those values. I would expect that cache should be invalidated and new styles should be calculated and applied to the children.
I was following the instructions from https://github.com/bvaughn/react-window/blob/master/website/README.md
However, I'm getting an error message when starting the dev server for the website:
Error: Cannot find module 'react-dev-utils/workspaceUtils'
In order to avoid adding cost to existing list and grid components, create a new variant (e.g. DynamicSizeList
and DynamicSizeGrid
). This variant should automatically measure its content during the commit phase.
The initial implementation of this could work similarly to how CellMeasurer
works in react-virtualized:
This component could perform better if we removed the third constraint above, allowing random access (by either item index or scroll offset) without measuring the preceding items. This would make react-window much more performant for use cases like chat applications.
This would also unlock the ability to use a ResizeObserver (via react-measure) to automatically detect item sizing and remove the position and measurements cache entirely. This would remove the need for imperatively resetting cached measurements and dramatically improve the API.
In order for the above to be possible, the dynamic list/grid components would need to use a dramatically different approach for mapping offset to index and vice versa. (This comment about "scroll anchoring" in react-virtualized has some nice visuals.) Essentially, we would need to do something like this:
Estimate total size based on the number of items multiplied by a estimatedItemSize
prop. (This estimated size won't need to be adjusted, since the mapping described below doesn't is fuzzy.)
When scroll position changes, compare the new offset to the previous offset. If the delta is greater than some [to be determined] threshold, set the new offset as the "scroll anchor". Map the offset to an estimated index (e.g. divide the offset by total estimated scrollable size and multiply that by the number of items in the collection). Store this mapped index as the "anchor index". For example, if the list described by the image below had 250 items, the "anchor index" would be 132.
The above approach has only one major downside: aligning items correctly at list boundaries. If item indices are estimated (as described above) then they likely won't line up exactly with the beginning or end of the scrollable area.
The end could potentially be accounted for by adjusting the total estimated size as the user scrolls closer to the end (although this might make scrolling feel janky).
The start of the list is harder to handle, since the first item needs to align with offset zero while still appearing to connect contiguously with items from some offset greater than zero. Perhaps another threshold could be used, a "safe zone", near the start of the list (e.g. if the scroll offset is less than some absolute value) which would force the list to measure all cells up to that point so they align correctly. The cost of this forced measurement would be relatively low, since it would only be a small number of items.
The one case that would still not be handled correctly with the above approach would be a scroll anchor that is set outside of the "safe zone" but a current scroll that goes inside of the safe zone (as shown below). If the user scrolls slowly back toward the beginning of the list, it may be difficult to align the first cell with zero without introducing scroll janky.
May be it's well known issue, but for safari IOS all the controls with both scroll (horizontal and vertical) allowed in one div become a pain.
Scrolling because of scroll elasticity looks like
It's reproduced on real devices, and all that jumps too. Unfortunately safari has no some kind of directionalLockEnabled
feature, and does not support overscroll-behavior
css prop.
To avoid we splitted scrolls across 2 different divs (one with y scroll enabled and other with x), in that case scroll works in one general direction, and there is no such jumps.
The problem of such solution that it hides right scroll from a window (horizontal scroll is placed at the right of table which is wider than window)
Just a question haven't you think about this problem and possibly know the solution?
Thank you for this lib, amazing!
Just asking because i'm curious about the difference between different implementations of virtualized lists and was pleasantly surprised to see react window being only 92b
, but then saw a more recent release (1.0.0-alpha.3) that shot it up to 5.3kb
:
https://bundlephobia.com/[email protected]
The other implementation I was looking at is sitting at 3.5kb
:
https://bundlephobia.com/[email protected]
Is there a way to have padding at the top and bottom of a list? If I do 10px 0
on the outer div I get 20px
of padding at the bottom, I can make it look proper by adding a 10px
top margin to the list items but that just shifts the windowing off-center.
This would make them easier to update when the API changes.
Ideally I would like to avoid embedding them into the CRA website though since this would bloat the website size significantly.
Maybe I could use something like CodeSandboxer to create lightweight links to CS:
https://github.com/Noviny/codesandboxer/tree/master/packages/codesandboxer
Hi there :)
Thanks for this great lib!
I'm experimenting with it and I wondered whether it's somehow possible to animate the scrolling behavior ("smooth scroll") when using scrollTo()
/ scrollToIndex()
?
I'm working on a select component with VariableSizeList
of react-window
and it works perfect, but when I search to narrow down the result (and the menu's height will update), the innerRef's clientHeight is wrong as getEstimatedTotalSize
is not works right with dynamic data
const getEstimatedTotalSize = (
{ itemCount }: Props,
{ itemMetadataMap, estimatedItemSize, lastMeasuredIndex }: InstanceProps
) => {
let totalSizeOfMeasuredItems = 0;
if (lastMeasuredIndex >= 0) {
const itemMetadata = itemMetadataMap[lastMeasuredIndex];
totalSizeOfMeasuredItems = itemMetadata.offset + itemMetadata.size;
}
const numUnmeasuredItems = itemCount - lastMeasuredIndex - 1;
const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedItemSize;
return totalSizeOfMeasuredItems + totalSizeOfUnmeasuredItems;
};
The problem is that if the itemCount changes from 100 to 1, lastMeasuredIndex
is larger then 1, so we can't rely on it anymore
Currently I solved the problem by pass key={itemCount}
to List
to make a refresh calculation. So perhaps that's not a bug, but I think we should denote that it could be problematic with dynamic data
Love the size and focus of this library. Been looking for something like this to use as simpler foundation for these type of components. Thanks for reworking your ideas in this form!!
Perhaps this new version would include column headers for the Grid control. Use rowIndex = -1 for the header row. On each render of the Grid, rowIndex -1 would be the first row called for.
Codesandbox examples contains key in children function
It's not needed.
Also version set is alpha
fixed in #24
Hi :)
I was wondering whether you'd consider to allow actually setting overscanCount
value of 0.
Currently the required minimum is 1
to enable tabbing, which is completely understandable.
However, I am currently working on a case where the rendered items are rather expensive and tabbing is not important/disabled on purpose so I'd really appreciate a way to override this.
Thanks for your consideration!
I created a complex Table
component based on Grid
of react-virtualized
, I'm rewriting it now, and I'm not sure that should I use this library directly or wait for it being merged into react-virtualized
as v10
.
I love the simplicity of this library compare to react-virtualized
, seems the original purpose of this repo is an api experimental for react-virtualized
v10, I'm using some apis like onScrollbarPresenceChange
from react-virtualized
which are not available in this library, but I could implement them on my side if I choose to use this library. So I want to make sure what's the future of this library.
When I log the children component props,
const Row = (props) => {
console.log('-----', props)
return (
<div>#{props.index}</div>
)
}
the data prop in props is undefined, why an undefined value is passed to the children component?
Here is the source code
// ...
createElement(children, {
data: itemData,
key: itemKey(index),
index,
isScrolling: useIsScrolling ? isScrolling : undefined,
style: this._getItemStyle(index),
})
// ...
The itemData
is from the props.
Calling e.g. ref.current.resetAfterColumnIndex()
on VariableSizeGrid
seems to trigger a runtime error.
Resize the window of this Code Sandbox to repro: https://codesandbox.io/s/5ywr9ro7nn
It looks like the this
context is getting messed up somehow. (this
is valid but _this
is undefined.)
I'm seeing a lot of white while scrolling (screen cap below) in a list of 3xx items. They're all connected items, though (as in they're bound to a store), does this make a difference? Are elements re-created or cloned in order to virtualize them? I don't have that problem in the react-window demos, and they seem to have tons of items more, i wonder what's causing it for me ... 🤔
And a quick screenshot of the code that produces the list:
Example:
https://codesandbox.io/s/vnn4z83313
When scrolling down you can see the scrolling indicator jump up and it becomes more and more pronounced when reaching the bottom of the container
Document an AutoSizer
solution built with react-measure (based on the Resize Observer spec, can be polyfilled). This seems like a more future-friendly way of detecting resize.
For the moment, I have published a standalone version of the AutoSizer
component from react-virtualized, as react-virtualized-auto-sizer.
Is it possible to fetch more data from the remote location if the scroll is at the end? If yes, how would I build this? If not I would love to help adding that feature 👍 .
Is it compatibe with IE11?
Hey,
I recently ported a list over to react-window and I noticed that under certain circumstances the itemSize callback can be called with an invalid index.
My code is basically
<VariableSizeList
height={height}
width={width}
ref={this._setRef}
onItemsRendered={this._onItemsRendered}
itemCount={this.props.rows.length}
itemSize={this._itemSizeGetter}
estimatedItemSize={56}
>
...
_itemSizeGetter = index => {
if (this.props.filter.length) {
return 56
}
const row = this.props.rows[index]
so itemCount is based on this.props.rows so that should always be synced up.
From what I've seen itemCount is 0
and since (https://github.com/bvaughn/react-window/blob/master/src/VariableSizeList.js#L42)
for (let i = lastMeasuredIndex + 1; i <= index; i++) {
let size = ((itemSize: any): itemSizeGetter)(i);
the i <= index
allows this to be out of bounds
Hi,
What I'm looking to do is have each items height be dynamic based on it's content.
This can be achieved by getting the clientHeight
value from the refs of list items, capturing that to state (as an array or map object) and passing that value back to the itemSize
function. However, a value itemSize
is initially required. If I give it a value, then the clientHeight
will always be that value.
Can anyone point me in the right direction? Much appreciated, thanks!
So far there are no automated tests running on CI.
Would be useful to add this.
Is it within the scope of this library to have some way to mark columns as being fixed/sticky?
Eg. having the cells in the first 2 columns of a VariableSizeGrid
not scroll horizontally while the rest of the cells do?
Currently I have other code that takes care of fixing these cells, but that is preventing me from using the *Grid
components to handle horizontal scroll windowing. Given the complexity of the some of the cell contents this can cause a the rows to take a long time to pop in while/after scrolling.
If this is not currently possible and within the scope of the library I would be willing to look at opening a PR to add this functionality.
Currently scrolling with Page Up / Page Down is not possible after you click on the list item.
Do you have an example of using this with tables? I'm trying to get it working but I have a strong suspicion that it either doesn't work, or that I'm doing something very wrong.
I've set the outerTagName to "table" and the innerTagName to "tbody", but I don't get any scrolling.
Here is my code, not sure if it helps (items is a list of objects):
<List
outerRef={this._list}
outerTagName="table"
innerTagName="tbody"
height={300}
itemData={items}
itemSize={() => 30}
itemCount={items.length}
itemKey={item => item.uniqueId}>
{({index, style, data}) => {
return (
<tr style={style} key={index}>
<td>Item {index}</td>
</tr>
);
}}
</List>
Relates to this Twitter thread:
https://twitter.com/Vjeux/status/1008077144289787904
Since the scrolling thread can get ahead of the UI thread, windowing libraries sometimes show blank space in the direction being scrolled (especially for sudden, quick scrolling).
It might be possible to use a heuristic based on the scroll delta and the time between commits (i.e. componentDidUpdate
calls) to render ahead of where the scroll position currently is. This might enable us to reduce the amount of visible white space when scrolling quickly.
This heuristic would need to take the state.scrollUpdateWasRequested
value into consideration so that programmatic scroll-jumps wouldn't cause us to render ahead.
I see in https://github.com/bvaughn/react-window/blob/master/src/createListComponent.js#L276 that you're using will-change: transform
. But from inspecting the demos it doesn't seems that transform
is used anywhere
So, what will-change
is used for?
I'm having a non-stop re-rendering.
In this simple example, I'm just rendering div containing "X".
I gave it the right height and width, tried to remove every possible CSS and I just can't get it.
my code:
<div>
<List
height={200}
itemCount={this.state.filteredValues.length}
itemSize={17}
width={682}
overscanCount={10}
>
{({ index, style }) => (
<div> X </div>
)}
</List>
</div>
Thanks for your work on this, looks really impressive 🎉
I am interested if you are open to add windowscroller functionality in this library or if that is out of scope for this library perhaps because of performance reasons.
In our app we make use of react-virtualized at this moment, but it's always great to switch to a more performant alternative. If I can find time, I am interested to help out with the implementation of the windowscroller functionality.
I am rendering a VariableSizeList
component which has fewer items than its height and therefore is not scrollable at first, but more rows are added later which then requires scrolling. However, the rows added are not shown.
Here is what I think what happens:
VariableSizeList
is rendered with a few items, which don't require scrollingscrollToItem()
(this is called after adding rows because I want the list to be always scrolled to the bottom.scrollOffest
in state set to a negative value (I see this from react-devtools)scrollOffset
they are assumed to be out of view and therefore not rendered.I did some logging from the library and see maxOffset
is negative here https://github.com/bvaughn/react-window/blob/master/src/VariableSizeList.js#L191
When manually setting the scrollOffest
to 0 using react-devtools the missing rows are rendered.
Is there a case why the scrollOffset
should ever be negative? Otherwise, I would suggest adding Math.max(0, maxOffset)
there.
This might also affect FixedSizeList
or the Grid components, I haven't tested with them.
I am facing a problem like No items displayed while scrolling also scroll bar keeps moving,
I just followed the guide from the site, other lib i used are antd
and styled-components
;
Here is codesandbox example: https://codesandbox.io/s/7579z7om6
We already have react-virtualized, So I want know why build another library again;
I believe many people want know the reason.. thank you @bvaughn
First thanks for the great work on that library!
I was asking myself a question about items animation. I have been using react-virtualized
so far, but it also applies to react-window
.
The current positioning strategy is to use absolute offset based on scroll position, right? Which makes it difficult to use CSS techniques to animate items. JS techniques are also problematic if they involve re-rendering the whole list each frame.
Do you have a suggestion on the best way to implement this? Typically, think about a contact card in a contact list, that can be expanded with more info.
Do you think static positioning would work? For example, by adding a transparent div the size of the items prepending the windowed items? (yes I mean me developing another custom solution for my specific purpose, but I wanted to ask the question as you explored this a lot more and migh see the obvious issues with this strategy :))
Hi There! Big fan of the library so far. I'm having an issue getting the ref logic to work for the scrollTo
functionality (trying to mimic ScrollSync functionality from react-virtualized).
Also, please let me know if this is a poor use of issues.
React Version: "16.4.2"
React Window: "1.1.2"
The <Grid>
component is calling this.syncHeaderScroll
in the onScroll
prop.
I was hoping syncHeaderScroll would look something like this:
syncHeaderScroll = ({ scrollLeft }) => {
if (this.listRef.current) {
this.listRef.current.scrollTo(scrollLeft)
}
}
I'm trying to make sure I'm not missing anything. When I console.log this.listRef.current
I'm just getting null.
Code snippet here:
export class ActivityStream extends PureComponent {
listRef = React.createRef()
syncHeaderScroll = ({ scrollLeft }) => {
console.log("this.listRef.current", this.listRef.current)
}
render() {
return (
<Fragment>
<List
direction="horizontal"
ref={this.listRef}
height={HEADER_HEIGHT}
itemCount={ACTIVITY_STREAM_HEADERS.length}
itemData={ACTIVITY_STREAM_HEADERS}
itemSize={index => ACTIVITY_STREAM_COLUMN_WIDTHS[index]}
width={tableWidth}
>
{props => <HeaderRenderer filters={filters} onOpenFilter={this.handleOpen} {...props} />}
</List>
<Grid
columnCount={ACTIVITY_STREAM_HEADERS.length}
columnWidth={index => ACTIVITY_STREAM_COLUMN_WIDTHS[index]}
estimatedRowHeight={DEFAULT_ROW_HEIGHT}
height={windowHeight - 250}
itemData={itemData}
overscanCount={10}
rowCount={rowData.length}
rowHeight={index => {
const { first_session_activity, last_session_activity } = rowData[index]
if (first_session_activity && last_session_activity) {
return DEFAULT_ROW_HEIGHT + DIVIDER_BOTTOM_MARGIN + BORDER_WIDTH * 2
}
if (first_session_activity) {
return DEFAULT_ROW_HEIGHT + DIVIDER_BOTTOM_MARGIN + BORDER_WIDTH
}
if (last_session_activity) {
return DEFAULT_ROW_HEIGHT + BORDER_WIDTH
}
return DEFAULT_ROW_HEIGHT
}}
width={tableWidth}
onScroll={this.syncHeaderScroll}
>
{ItemRenderer}
</Grid>
</Fragment>
)
}
I am trying to show a spinner at the end of the list when more data is getting fetched.
I can't seem to find a better way other than adding a footer component after inner container:
<outer>
<inner><item/>...</inner>
<footer/>
</outer>
@bvaughn is this something that you would want to add?
Compare the following builds:
[email protected]
(cell style cache object)[email protected]
(ListItems
+ cell style cache object)[email protected]
(ListItems
+ ListItem
)The new React Profiler
component could come in handy for this, particular once the DevTools integration has been completed.
Hi Brian,
thanks for an awesome work.
Is it possible somehow to scroll smoothly? e.g. scrolling will not jump directly to a specified row.
Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.