I was trying to use the combination of the WindowScroller + AutoSizer + List on my web application. The virtualization works well when it is only AutoSizer + List. However, when I put it inside the WindowScroller, the list of rows no longer display properly.
This is how it looks when WindowScroller is applied.
list result
I already searched for available solutions here and on the doc. Found similar problem posted here but the answer provided was already been applied on my code. Now, I can't figure out what exactly hinders the display of the rows.
return (
<React.Fragment>
<WindowScroller>
{({height, isScrolling, onChildScroll, scrollTop}) => (
<AutoSizer disableHeight>
{({width}) => (
<List
autoHeight
height={height}
isScrolling={isScrolling}
onScroll={onChildScroll}
rowCount={rows.length}
rowHeight={30}
rowRenderer={({ index, style }) => <div style={style}>Row {index}</div>}
scrollTop={scrollTop}
width={width}
>
</List>
)}
</AutoSizer>
)}
</WindowScroller>
</React.Fragment>
);
It appears this is an implementation issue. The overflow style of the container element which I was using for the reference of the WindowScroller is not set properly.
Related
I am using React-Virtualized to display a table with a long list of values. So it's a combination of WindowScroller, AutoSizer and Table. I am having an issue when the browser is resized. This is my code:
render() {
return (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer>
{({ width }) => (
<Table
ref={(ref: Table) => { this.TableRef = ref; }}
autoHeight={true}
height={height}
width={width}
isScrolling={isScrolling}
scrollTop={scrollTop}
_noRowsRenderer={this._noRowsRenderer}
...
>
<Column
...
/>
</Table>
)}
</AutoSizer>)}
</WindowScroller>
);
}
When the browser is resized the width of the table is not updated accordingly and so a vertical scroll bar is being displayed although it's not needed; unless it's zoom-in or zoom-out and then it's all redrawn-positioned correctly.
Does anyone has any idea how to fix this?
You were right that I was not passing the props in correctly. Now I have it set up as such:
Container.jsx
<div className='container' ref={(ref) => {this.foo = ref;}}>
this.renderContainer()
</div>
<Section scrollContainer={this.foo}/>
Section.jsx (just passing down props)
<Panel scrollContainer={this.props.scrollContainer}/>
Section.propTypes = { scrollContainer: PropTypes.object.isRequired }
Panel.jsx (Passing down props)
<RenderedTable scrollContainer={this.props.scrollContainer} />
RenderedTable.jsx
return (
<div className='padding-top-20 font-smoothing'>
<WindowScroller scrollElement={this.props.scrollContainer}>
{({ height, isScrolling, scrollTop, onChildScroll }) => (
<AutoSizer disableHeight>
{({ width }) => (
<Table
Unfortunately the windowScroller still does not resize. I also dont get any warnings or errors. Do you use css tricks to get the scroller to resize? I see that in the example https://bvaughn.github.io/react-virtualized/#/components/WindowScroller
you change the flex and overflow properties when changing the scrollElement from window to scrollingBody.
I know you are very busy and greatly appreciate your help!
In your example, you've assigned the scroll ref to this.tabsContainer but you're trying to access it as this.props.tabContainer. Either this is your mistake, or the example is incomplete and more context is needed. :) Can you provide a Plnkr?
In docs - headerRowRenderer, but can anyone share simple example with some custom header markup, for example with custom title attr + all 'default' virtualized features, like sortable...
Your question mentions headerRowRenderer but I think you might actually be asking about how to render a custom header cell based on the rest of your statement. Anyway, I'll show both.
// This is a custom header row rendered
// You should used all of the specified params,
// But you can also add your own decorated behavior.
const headerRowRenderer = ({
className,
columns,
style
}) => (
<div
className={className}
role='row'
style={style}
>
{columns}
</div>
)
// This is a custom header example for a single cell
// You have access to all of the named params,
// But you don't necessarily need to use them all.
const headerRenderer = ({
columnData,
dataKey,
disableSort,
label,
sortBy,
sortDirection
}) => (
<div>#</div>
)
const renderTable = (props) => (
<Table
{...props}
headerRowRenderer={headerRowRenderer}
>
<Column
dataKey='number'
headerRenderer={headerRenderer}
width={100}
/>
<Column
dataKey='name'
label='Name'
width={200}
/>
</Table>
)
Here's a Plnkr example for you: https://plnkr.co/edit/eHr3Jr?p=preview
To render a custom header, you can use React-Virtualized's Column component by passing headerRenderer prop to it.
headerRenderer is a callback responsible for rendering a cell's header column.
Here is an example showing implementation:
Method to create a custom header by returning JSX; to be declared above the render method.
You can return JSX, according to your requirements. In this example we return a paragraph (p tag).
formatCheckboxHeader = () => {
return (
<p>Custom Header</p>
)
}
In the render method, where the react-virtualized table is initialized.
<Column
width={100}
headerRenderer={this.formatCheckboxHeader}
label='#'
dataKey='id'
cellRenderer={({ rowData }) => this.formatIdColumn(rowData)}
/>
Additionally, you can pass rowData to the headerRenderer as done in the cellRenderer
Read more about headerRenderer here.
I would like to render a list of items using react-virtualized, however some of the items could change the size.
I have added a demo, where each item has a button and when someone clicks the button then the item needs to expand and push the below items lower:
https://plnkr.co/edit/GG2bSIC5M1Gj7BR1pOii
The use case is similar to facebook posts, when someone comments it will expand the post and push the other posts lower.
ReactDOM.render(
<List
className='List'
autoHeight
width={300}
height={400}
rowCount={list.length}
rowHeight={30}
rowRenderer={
({ index, isScrolling, key, style }) => (
<div
className='Row'
key={key}
style={style}
>
{list[index]}
<Expander /> /* contains the button, which when click it will expand more text*/
</div>
)
}
/>,
document.getElementById('example')
Is any way to achieve this?
UPDATE
I am realizing that there must be a way to force the List to update items and recalculate positions. There is an example with InfiniteLoader (react-virtualized example) which is using registerChild in the List as a ref. And it seems that when InfiniteLoader receives answer from the server it capable of forcing list to re-render rows.
I have tried another example with forceUpdate on list, however the List is still not updating.
https://plnkr.co/edit/RftoShEkQW5MR5Rl28Vu
As per the Documentation forceUpdateGrid() should be called instead of forceUpdate().
https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#public-methods
I faced the similar issue and solved it by calling forceUpdateGrid() inside componentWillReceiveProps method.
componentWillReceiveProps(){
this.refs.forceUpdateGrid();
}
render(){
<List
ref={ref => this.refs = ref}
.....
/>
}
Try using Collection from react-virtualized instead of List. It should help :)
add key={Math.random()} the parent of the list item solved my issue.
<ListWidget key={Math.random()}>
<Autosizer>{
.....
<List
.....
/>
.....
}</Autosizer>
</ListWidget>
Note: not recommended, it's just a workaround to get PR accepted
Is it possible to use react-virtualized and enzyme together? When I try to use them together I seem to get an empty list of items in the grid.
The 2 should work together, yes. I believe the likely problem is that the react-virtualized component is being given a width or height of 0 which causes it not to render anything. (It only renders enough to fill the "window" it has.)
Assuming you're using the AutoSizer HOC- (most people do)- then one pattern I've found helpful is to export 2 versions of components- one that expects explicit width/height properties and one that wraps the other with an AutoSizer. Pseudo code would be:
import { AutoSizer, VirtualScroll } from 'react-virtualized'
// Use this component for testing purposes so you can explicitly set width/height
export function MyComponent ({
height,
width,
...otherProps
}) {
return (
<VirtualScroll
height={height}
width={width}
{...otherProps}
/>
)
}
// Use this component in your browser where auto-sizing behavior is desired
export default function MyAutoSizedComponent (props) {
return (
<AutoSizer>
({ height, width }) => (
<MyComponent
height={height}
width={width}
{...props}
/>
)
</AutoSizer>
)
}
as of react-virtualized 9.12.0 the Autosizer has defaultWidth and defaultHeight properties.
I found setting those meant enzyme tests ran correctly - rendering the child rows as expected.
<AutoSizer disableHeight defaultWidth={100}>
{({ width }) => (
....
)}
</AutoSizer>
Putting this in my test case worked for me:
import { AutoSizer } from 'react-virtualized';
// ...
it('should do something', function() {
spyOn(AutoSizer.prototype, 'render').and.callFake(function render() {
return (
<div ref={this._setRef}>
{this.props.children({ width: 200, height: 100 })}
</div>
);
});
// do something...
I use Jasmine's spyOn here, but other libraries have their own ways of overwriting functions.
Keep in mind that this is pretty fragile against future changes to the react-virtualized library (this._setRef was just yanked from the source code), and may give you false positives.