I am using react-virtualized to create an infinite scroll in my React app. Because list items can be of different heights, I am using CellMeasurer from react-virtualized to render list items. I also have an intersection observer that listens for scroll and loads more data as user scrolls down. The code looks like this:
const rowRenderer = ({ key, index, style, parent }) =>
<CellMeasurer
cache={cache.current}
parent={parent}
columnIndex={0}
rowIndex={index}>
<ListItem
key={key}
listData={listData[index]}
style={style}
setObserver={setObserver}
/>
</CellMeasurer>
<AutoSizer>
{
() => ({ width, height }) =>
<List
width={width}
height={height}
rowHeight={cache.current.rowHeight}
deferredMeasurementCache={cache.current}
rowCount={listData.length}
rowRenderer={rowRenderer}
/>
}
</AutoSizer>
This works fine except React gives a warning that each item of List should have a unique key. If I move the key={key} to CellMeasurer from ListItem to fix this, the warning goes away but the code doesn't load new data anymore on scroll.
I am not sure why this is happening?
The app also allows deleting a list item from the list. However, when the deletion happens, the list scrolls down by a significant number of rows, rather than staying at that scroll position. How do I fix this?
Related
So I have this selection FlatList where the user can select a country. When I press on a item it's going to take a second and after the second it is displaying the checkmark.
The Problem is the second. The user cannot wait a second to see that the country was selected.
<FlatList
keyExtractor={item => item.countryid}
data={countryChoices}
renderItem={({item}) => {
return(
<CountryComponent
item={item}
conditionToCheck={country == item.countryname}
onPress={() => setCountry(item.countryname)}
/>
)
}}
/>
This is my Flatlist. countryChoices is just an array with different objects for the countries. the conditionToCheck checks wether to show or to hide the checkmark. If the selected Country is equal to the item then it's going to show the checkmark.
But after clicking on it it's taking too long :/
I also tried wrapping the FlatList Item in a useCallback but it wasn't (much) faster
Try passing country as extraData in you FlatList since country is outside of data prop
<FlatList
keyExtractor={item => item.countryid}
data={countryChoices}
extraData={country}
renderItem={({item}) => {
return(
<CountryComponent
item={item}
conditionToCheck={country == item.countryname}
onPress={() => setCountry(item.countryname)}
/>
)
}}
/>
extraData is a marker property for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.
Goal
I'm currently attempting to use React-Transition-Group to animate the rows of a table in and out. There are two different scenarios that can happen:
the entire contents of the table will be swapped out (most common option)
individual rows may be added and removed
Where I am now
I used this todo list example as a starting point to make the table animation. Here is my sandbox.
My main problem is when the table data is being completely swapped out, you can see both data sets at the same time. The old data is sliding out while the new data is sliding in. This causes the old data to snap down as it's transitioning out (see image below).
Ideally the initial table data would completely disappear before the new data comes in, but instead the new data pops in while the old data is there.
Code
Table Prop that maps through rows
<table>
<TransitionGroup component="tbody">
{this.props.tables[this.props.currentTable].rows.map((row, i) => {
return (
<CSSTransition
timeout={500}
classNames="SlideIn"
key={`${row}-${i}`}
>
<tr>
{row.map((column, i) => {
return <td>{column}</td>;
})}
</tr>
</CSSTransition>
);
})}
</TransitionGroup>
</table>
Function to change table dataset:
changeTable = () => {
this.setState({
currentTable:
this.state.currentTable < this.state.tables.length - 1
? this.state.currentTable + 1
: 0
});
};
I have recently started using react-native and have come across the FlatList component. When using react I have always used map with an array.
I was using FlatList but there was issues when I wanted to change the flex-direction of items in the FlatList so I reverted to using map.
Here are the two examples using both methods:
map
{
this.state.images.map(image => {
return (
<UsersImage key={ image } source={{ uri: image }} />
)
})
}
FlatList
<FlatList
data={ this.state.images }
renderItem={({item}) => {
return (
<UsersImage source={{ uri: item }} />
)
}}
keyExtractor={(item, index) => index}
/>
Can anyone explain why one should use FlatList over map or vice versa?
FlatList has lazy loading (it only shows what's on screen, so it can be more performant if you have a huge list). BTW you can get it to be horizontal if that's what you needed to change the flex direction for - just pass the horizontal prop (equivalent to saying horizontal={true})
The FlatList displays the similarly structured data in a scrollable list. It works well for large lists of data where the number of list items might change over time. The FlatList shows only those render elements that are currently displaying on the screen, not all the elements of the list at once. So that's the main reason FlatList can handle a large set of data with a very complex view easily.
The map() function is used to iterate over an array and manipulate or change data items. In React, the map() function is most commonly used for rendering a list of data to the DOM.
In react-native FlatList is recommended over map() when we need to display a large scrollable list.
no need to flex your View tags. just add horizontal
<FlatList
horizontal
data={data}
renderItem={(item) => this.renderItem(item.item)}
keyExtractor={(item, index) => index}
/>
I'm currently trying to highlight the top element in a react native ListView.
Is it possible to for ListView identify which row component is located at the top of the view on scroll?
If not, how would I go about selecting the lowest integer rowID of the visible rows?
renderRow(rowData, sectionID, rowID) {
return (
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
/>
);
}
You can use onChangeVisibleRows which gives you a list of the visible rows and the rows that have changed their visibility. Something like this should do:
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
onChangeVisibleRows={(visible, changed) => this.highlightRow(visible[0])}
/>
You would probably need to update the data source with the item you want to be highlighted containing a state that will indicate it.
My list component should be able to highlight why a certain element is part of the list based on keywords entered by the user.
I am using the react-highlighter component currently inside the list item, but it requires me to send down the keyword entered by the user from the search box => to the listview => to the list item
That doesn't look very react to me.
I am also using redux so I have only one store, and the listview and items are not directly aware of the store. They are dumb components just rendering properties.
<SearchBox onChange={setSearchText} value={searchText} />
<List items={item} highlight={searchText}>
<ListItem>
<Highlight search={searchText}>{name}</Highlight>
</ListItem>
</List>
Is there a more elegant way to handle such highlighting?
As you mentioned in a comment you could pull up your highlight functionality into a HighlightedListItem component.
<SearchBox onChange={setSearchText} value={searchText} />
<List items={item} >
<HighlightedListItem item={item} highlight={searchText}>
{...stuff}
</ListItem>
</List>
In addition when thinking ahead to maximizing performance, this approach might be easier to reason when adding a shouldcomponentupdate
EX:
// HighlightedListItem
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.item !== this.props.item ||
nextProps.searchText !== this.props.searchText;
}