FlatList only updates after user interaction occurs - react-native

I have a component that gets data from navigation props which I use to set the state in CDM():
this.setState({
data: this.props.navigation.getParam('data', 'Some Data')
})
The state is then used in a FlatList. When props change.. the FlatList doesn't update... Until the client touches the screen.
I have tried putting the props directly into the FlatList:
data={this.props.navigation.getParam('data', 'Some Data')}
I have tried using the extraData prop:
<FlatList
style={{ flexGrow: 1, marginTop: 10 }}
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
extraData={this.props.navigation.getParam('data', 'Some Data')}
renderItem={({ item }) => {...}>
I have tried using componentDidUpdate() to reset state when the props change but CDU never gets called.
EDIT:
I have a feeling it is because when I add items; I'm navigating back (navigation.goBack())
and goBack() dosnt refresh the state? i'm not sure.

Found it:
https://github.com/react-navigation/react-navigation/issues/922#issuecomment-344752635
It's essentially passing a function as a parameter and calling that function to change the state of the parent then calling goBack()

Related

React native FlatList not rerendering when data prop changes to empty array

I have a FlatList with a data prop pulling from Redux
render() {
return (
<View>
<FlatList
data={this.props.arrayOfPlacesFromRedux}
renderItem={({item}) => {.......
Whenever I dispatch changes to arrayOfPlacesFromRedux(i.e. adding or removing children), the FlatList rerenders....UNLESS I remove all children from array (i.e. make length zero).When arrayOfPlacesFromRedux changes from a positive length to a length of zero, the FlatList does not rerender.....however all other types of changes to array do indeed cause FlatList to rerender
UPDATE 02/27
Below is my reducer used to update Redux arrayOfPlacesFromRedux
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_PLACES_ARRAY:
return {...state, arrayOfPlaces: action.payload};
default:
return state;
}
};
In the situation noted above when FlatList does not rerender.....action.payload is an empty array
The question is missing some important piece of code.
React as well as Redux need arrays reference to change, meaning for a component to reRender on state change, the array references needs to change.
Live demo at https://snack.expo.dev/RrFFxfeWY
Here is the most interesting parts:
If you have a basic component as below:
const MyList = () => {
const [data, setData] = React.useState([
'#FF0000',
'#FF8000',
'#FFFF00',
]);
return (
<>
<Text>List poping is not working</Text>
<FlatList
data={data}
renderItem={({ item }) => (
<Pressable
onPress={() => {
data.pop(); // Does not work because we are not changing it's ref
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
The data need to have a new array reference as below. data2.filter(..) will return a new array, we are not changing the data2 base values, just creating a new array with one item less.
const MyList = () => {
const [data2, setData2] = React.useState([
'#00FFFF',
'#0080FF',
'#0000FF',
]);
return (
<>
<Text>List WORKING!</Text>
<FlatList
data={data2}
renderItem={({ item }) => (
<Pressable
onPress={() => {
setData2(data2.filter(dataItem => dataItem !== item)) // works
//setData2([]); // Also works
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
A library like Immer.js simplify the manipulation of states to mutate the object, and immer will created a new reference for you.
Oh no rookie mistake that wasted everyones time!!
I was implementing shouldComponentUpdate method that was stopping Flatlist rendering :(
Thanks for all for the answers
You may need to use ListEmptyComponent, which is a prop that comes with FlatList, src.
Honestly, I'm not sure why it does not re-render when you update your state, or why they added a specific function/prop to render when the array is empty, but it's clear from the docs that this is what's needed.
You can do something like this:
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
--> ListEmptyComponent={() => <MyComponent />}
/>
</SafeAreaView>

Flatlist event when start reached

How we can implement onStartReached in FlatList ?
I used horizontal list.
I have some idea of using onScroll using,but I thing this not properly.
Seems onRefresh could do the job.
https://reactnative.dev/docs/flatlist#onrefresh
it must be start reached and pull to trigger onRefresh.
If you need a infinite scroll i used this
<FlatList
data={data}
renderItem={({ item, index }) => {
return (
// Component to render
);
}}
keyExtractor={(item) => item.uid}
onEndReached={() => //Load more data}
onEndReachedThreshold={0.5} // "Sleep" time
initialNumToRender={10} // initial number to items
/>
otherwise the way to use onStartReached is identical to onEndReached

Does reference of any component available before even rendering it at least once?

Lets say I a have FlatList component as follows,
<FlatList
ref={ref => this.flatListRef = ref}
data={this.state.data}
renderItem={({ item, index }) => this.renderLeaveRequests(item, index)}
keyExtractor={item => `${item.id}`}
onEndReached={this.loadMore}
/>
I Want FlatList to go to some particular item based on offset and I achieved like follows,
componentWillMount() {
this.flatListRef.scrollToOffset({ offset: scrollPosition, animated: false });
}
If you observe I am calling scrollToOffset in componentWillMount which means before rendering the component. Here my doubt is how come reference of FlatList is available before rendering it.
Does reference of any component available before even rendering it at least once?
No, because you assign your ref in the render, this isn't avalaible before rendering.
Use componentDidMount instead

Why React Native FlatList style not change with state

I have some tabs. I want to change the color of the selected tab. I created a state for selected tab index which will hold the tab ID. When Tab is pressed the selected state change to the pressed tab ID. I am comparing the selected state to tab ID. If both are equal then the selected tab will have some different style.
But when state changes and condition is true, the selected tab is not changing its state. Why change in state do not trigger the comparison in the style to update the style?
<FlatList
horizontal
data={this.state.drinkgroup}
showsHorizontalScrollIndicator={false}
renderItem={({item, index}) =>
{
return <Tab
item={item}
selected={this.state.selected}
changeSelected={
() => {
this.setState({selected: item.id}, function(){ console.log(this.state.selected, item.id)
console.log(this.state.selected==item.id)
})
}}
}
}
/>
export const Tab = ({item, selected, changeSelected}) => {
return (
<TouchableOpacity
style={[styles.tabStyle, (selected==item.id)? styles.tabSelectedStyle: null]}
onPress={changeSelected}
underlayColor='#fff'
>
<Text style={{color: '#f2f2f2', textAlign: 'center', fontSize: 15}}>{item.name}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
tabStyle: {
backgroundColor: '#800080',
height: 32,
paddingRight: 15,
paddingLeft: 15
},
tabSelectedStyle: {
borderBottomColor: 'white',
borderBottomWidth: 3
}
})
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
<FlatList
data={this.props.data}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
This is a PureComponent which means that it will not re-render if props remain shallow- equal.
Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes.
This includes the data prop and parent component state.
More Details :- FlatList
You need to provide extraData prop to the Flatlist, if you want it to re render its items. You can do it like extraData={this.state}.
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this extraData prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
For further details you can visit official documentation here

Flatlist + React Native Router Flux: slow navigate transition

I have a datasource with more than 800 entries which I'm using FlatList to render it.
Each renderItem receives a function to navigate to another screen on item press.
The problem is that the transition between screens is extremely slow.
I noticed that even with scrolling working fast, renderItem is still being called for all 800 entries in DOM. When all items are finally rendered, then the navigation works fine.
I've tried using initialNumToRender, getItemLayout and waitForInteraction props, as well tried to change my renderItem component (now is a stateless component) to a pure component. Nothing seems to work so far.
Any suggestion will be appreciated.
Here's some code if may help:
<FlatList
data={this.state.listDataSource}
renderItem={({ item, index }) => this.renderListItem(item, index)}
keyExtractor={this._keyExtractor}
style={{
flex: 1,
marginHorizontal: 30,
borderTopWidth: 1,
borderColor: '#919191',
}}/>
renderListItem(item, index) {
return <ListItem dotFunc={() => this.onListItemPress(index)} item={item} />;
}
onListItemPress(index) {
Actions.itemDetail({
index
});
}
// ListItem.js correctly exported
const ListItem = ({ dotFunc, item }) => (
<TouchableOpacity onPress={() => Actions.contactDetail({rowID})}>
<Text>{Item}</Text>
</TouchableOpacity>
}
Thanks