I'm using 'react-virtualized' to render an infinite scrolling list.
However, I am having trouble rendering the rowHeight dynamically. I made an attempt, but it only is relevent for desktop, and feels wrong.
I tried following the examples, but had no success.
What is the correct way to calculate the true row height?
It should be responsive to mobile.
Here is an example:
https://codesandbox.io/s/ADARgvlxB
class App extends React.Component {
render() {
return (
<div>
<AutoSizer>
{({ height, width }) => (
<List
height={600}
width={width}
rowCount={foo.length}
rowHeight={({ index }) => {
const x = foo[index];
if (x.name.length < 10) { return 20; }
else if (x.name.length > 9) { return 40;}
}}
rowRenderer={({ index, style }) => {
const x = foo[index];
return (
<div key={index} style={style}>
{x.name}
</div>
);
}}
/>
)}
</AutoSizer>
</div>
);
}
}
What is the correct way to calculate the true row height?
This is what the react-virtualized CellMeasurer component is for. You can see a demo of it measuring dynamic heights here. The source code for the demo are the *.example.js files here.
Related
Introduction
I have a FlatList that renders a Tab View in its footer. This Tab View let the user switch between one FlatList or an other. So, these last are sibling FlatLists.
Problem
The first FlatList "A" has a greater height than the second one "B". When I choose the second list, its height is the same as the "A" FlatList's one.
I have reproduced the problem in a snack where you can see a similar code. I recognize that the code is a little long but too simple, just focus only on the parent FlatList (in the App component) and the two FlatLists that are rendered in each tab (at the end of the code)
QUESTION
Any ideas how to solve this issue? I don't know if the problem is in the styles or if I have to do something else to make this work (all flatlists have to have their own height, not the greater).
Thank you, I would really appreciate your help.
UPDATE 2022
const renderScene = ({ route }) => {
//
// 📝 Note: We are hidding tabs in order to avoid the
// "FlexBox Equal Height Columns" typical problem
//
switch (route.key) {
case "bitcoin":
return (
<View style={index !== 0 && styles.hidden}>
<Bitcoin />
</View>
);
case "ethereum":
return (
<View style={index !== 1 && styles.hidden}>
<Etherum />
</View>
);
case "rose":
return (
<View style={index !== 2 && styles.hidden}>
<Rose />
</View>
);
default:
return null;
}
};
...
<TabView
renderTabBar={renderTabBar}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={handleOnIndexChange}
initialLayout={{ width: layout.width }}
removeClippedSubviews={false}
swipeEnabled
swipeVelocityImpact={0.2}
gestureHandlerProps={{
activeOffsetX: [-30, 30], // To solve swipe problems on Android
}}
style={globalStyles.flexContainer}
/>
Styles:
hidden: { display: "none" }
I have updated the snack with the solution!
As in the snack I implemented my own TabView, I have decided to implement the same solution with the library "react-native-tab-view", as it is the best tab for react native for now.
Think that some people having this issue will be able to solve it.
Basically, what we need to do is to dinamically calculate the height of each tab scene and pass it to the style of the TabView using the onLayout prop.
Just like this:
const renderScene = ({ route }) => {
switch (route.key) {
case "inifiniteScrollFlatList":
return (
<FirstRoute />
);
case "rawDataFlatList":
return (
<View
onLayout={(event) => setTab1Height(event.nativeEvent.layout.height + TAB_HEIGHT)}
>
<SecondRoute />
</View>
);
case "otherRawDataFlatList":
return (
<View
onLayout={(event) => setTab2Height(event.nativeEvent.layout.height + TAB_HEIGHT)}
>
<ThirdRoute />
</View>
);
default:
return null;
}
};
<TabView
style={ index !== 0 && {
height: index === 1 ? tab1Height : tab2Height,
}}
renderTabBar={renderTabBar}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
removeClippedSubviews={false} // Pd: Don't enable this on iOS where this is buggy and views don't re-appear.
swipeEnabled={true}
/>
Pd: You shouldn't do this with a tab that uses an infinite scroll with pagination. Instead, you will have to set the height to null to allow the parent FlatList to automatically get its height.
So, I haven't gone through the entire code, nor did I find a solution to your height problem.
But, what you can do is check out React navigation 5 -> createMaterialTopTabNavigator.
It lets you create tabs with separate flatlist for each tab. It will solve the height problem because what is being rendered are separate flatlists as per the active tab. And it will also, make your code much cleaner.
You won't have to use a Flatlist with header and footer components to render the tabs with nested Flatlists.
And if you want to hide the tabs on scroll, that is possible by passing props to the tab navigator that toggle visibility on scroll of the Flatlist using the onScroll event that is called when a Flatlist is scrolled. The same can be done for the visibility of the header as well. And with the proper animations it would look as if the header and the tabs were pushed up on scroll like it does right now.
I ended up removing the tabs content from the tab control altogether. It's hacky but worked for me...
render() {
const {index} = this.state;
return (
<ScrollView>
<TabView
renderPager={this._renderPager}
renderScene={() => null}
onIndexChange={index => this.setState({index})}
initialLayout={{height: 0, width: Dimensions.get('window').width}}
/>
{index === 0 && <Tab1Content />}
{index === 1 && <Tab2Content />}
</ScrollView>
);
Source:- https://github.com/satya164/react-native-tab-view/issues/290#issuecomment-447941998
this is my table code in AntD, On pagination clicked I want it to scroll to page top. I tried using BackTop but I couldn't figure out how to do that with pagination
const GenericTable = props => {
const { withHeader, pagination, showTable, error } = props;
return (
<Fragment>
{withHeader && <TableHeader {...props} />}
{showTable && (
<CustomTable
{...props}
pagination={{
...pagination,
showTotal,
}}
/>
)}
<TableFooter>{<Error>{error}</Error>}</TableFooter>
</Fragment>
);
};
GenericTable.defaultProps = {
pagination: {
pageSize: 10,
},
};
Antd's table have a onChange method, by using that you can scroll to top by java script.
use
document.body.scrollTop = 0; // For Safari
document.documentElement.scrollTop = 0;
CodeSanbox Link
Antd's table scroll top on pagination
You can always store pagination in state and compare it to latest one, to avoid scroll of table on changing of anything else in table like filter or sorting.
I hope this would help.
Thanks
Does react take into account CSS when rendering height? I have this code:
componentDidUpdate() {
let height = ReactDOM.findDOMNode(this).offsetHeight;
console.log(height);
sendResizePopupMessage();
}
Now the first time I click the button, I get height = 250. The next time I get height 277. In both scenarios ReactDOM.findDOMNode(this).innerHTML was the exact same, what would cause it to display different heights?
Here is my component:
<div className="popup-container container-fluid">
<PopupHeader
status={offerStatus}
disabled={disabled}
onCloseClick={() => this.handleCloseClick()}
onToggleClick={() => this.handleToggleClick()}
/>
{offers.length > 0 && this.state.shouldShowOffers && (
<PopupTable offers={offers}
onClick={() => this.handleFindCheaperClick()}
disableFindCheaper={this.state.disableFindCheaper}
findCheaperMessage={this.state.findCheaperMessage}
/>
)}
</div>
Given a react-virtualized List with variable content in each row the DOM height needs to be calculated by a rowHeight function - however since that gets called before the row is rendered I am unsure how to actually get the row height.
The examples given for dynamic List row height basically go off a predefined number in the list item's props which doesn't help.
What I think I want to do is render the row on the page at a default height and then get the overflow height on the DOM and set that as the row height. How can I hook into an afterRowRender function or something like that? I imagine performance would suffer so maybe there is a better way of doing this that I am missing.
Check out the docs on CellMeasurer. You can see it in action here.
Basically you'll be looking for something like this:
import React from 'react';
import { CellMeasurer, CellMeasurerCache, Grid } from 'react-virtualized';
const cache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 50,
});
function rowRenderer ({ index, isScrolling, key, parent, style }) {
const source // This comes from your list data
return (
<CellMeasurer
cache={cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
>
{({ measure }) => (
// 'style' attribute required to position cell (within parent List)
<div style={style}>
// Your content here
</div>
)}
</CellMeasurer>
);
}
function renderList (props) {
return (
<List
{...props}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
/>
);
}
I'm having an issue when I try to render a list of items as follows:
render()
{
const data = (my data);
const cellRenderer = ({index, key}) => (
<li key={key}>
<Datum data={data[index]} />
</li>
);
return (
<AutoSizer>
{
({ width, height }) => (
<CellMeasurer cellRenderer={ cellRenderer }
columnCount={ 1 }
width={ width }
rowCount={ transactions.length }>
{
({ getRowHeight }) => (
<List height={ height }
overscanRowCount={ 50 }
noRowsRenderer={ () => (<div> Nix! </div>) }
rowCount={ transactions.length }
rowHeight={ getRowHeight }
rowRenderer={ cellRenderer }
width={ width } />
)
}
</CellMeasurer>
)
}
</AutoSizer>
);
}
Now when I scroll down it skips every second list-item, until I end up with half the page empty but still scrollable.
When scrolling up it's even worse, it skips half the page.
AutoSizerand CellMeasurer seem to be working fine. I stepped through the code a bit, and it looks like they do create the correct measurement.
My data is just a static array of objects. Not a promise or stream.
I also changed my Datum component a few times to make sure it's completely static markup but that didn't change anything.
Any ideas anybody?
[edit]
Here's a Plunker showing my problem: https://plnkr.co/edit/2YJnAt?p=preview
Ironically, while fiddling with it, I accidentally figured out what I had done wrong. I'll submit an answer with my solution.
Right, I found the problem (and so did #MBrevda! +1!)
The rowRenderer method takes a style that needs to be applied to the rendered list element: https://plnkr.co/edit/FzPwLv?p=preview