I don't think this is a bug, more like I'm doing something wrong but here it goes. I'm building a Chat Component for my application, it's very simple, I type something press send button adds to the list and displays. Days ago I found that if I started typing big and quick messages my JS FPS would drop to like 10, 5 even 1 and I found out today thanks to implementing a data/time display for the messages that each time I type something in the TextInput and run onChangeText event and change the state of "text" it re-renders ALL the items inside my FlatList
Here is my code:
Chat Component:
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
onChangedText Function
onFooterTextChanged = (text) => {
this.setState({
text: text,
options_expanded: false,
});
};
onSendMessage Button Function
onSendButtonPressed = () => {
if(this.state.text !== null && this.state.text !== "") {
this.setState({
items: [
...this.state.items,
{
id: moment().valueOf(),
text: this.state.text,
date: moment().valueOf(),
user: {
id: globals.user_id_value,
avatar: globals.user_photo,
}
}
],
text: "",
options_expanded: true,
});
}
};
renderItem Function for FlatList
renderItem = (item) => {
const data = item.item;
const renderAvatar = this.renderAvatar(item);
const renderTime = this.renderTime(item);
if('text' in data){
return(
<ChatTextItem
keyy={data.id}
self={globals.user_id_value === data.user.id}
text={data.text}
user={data.user}
renderAvatar={renderAvatar}
sameUser={!renderAvatar}
renderTime={renderTime}
time={this.getTime(data.date)}
/>
)
} else if('image' in data) {
return(
<ChatImageItem
keyy={data.id}
self={globals.user_id_value === data.user.id}
image={data.image}
renderAvatar={renderAvatar}
sameUser={!renderAvatar}
renderTime={renderTime}
time={this.getTime(data.date)}
/>
)
}
};
Constructor if it helps
constructor(props){
super(props);
this.state = {
isLoading: false,
options_expanded: true,
text: "",
image: "",
items: [],
};
}
and I'm user PureComponent btw.
Edit #1: Console after typing like a madman in the TextInput It re-rendered 28 times for the 28 letters I typed and it does that * the number of items in the list already
Edit #2: Changes Made to the JS file
Changed FlatList extraData option
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
and changed the constructor to add refresh state
constructor(props){
super(props);
this.state = {
isLoading: false,
options_expanded: true,
text: "",
image: "",
items: [],
refresh: false,
};
}
Issue still persists
Edit: #3 Finally found the issue
Works
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
Doesn't Work
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
Any Ideas Why? Please!
If someone could help me find a solution to my problem I would appreciate it.
I don't think this is a bug, more like I'm doing something wrong but here it goes. I'm building a Chat Component for my application, it's very simple, I type something press send button adds to the list and displays. Days ago I found that if I started typing big and quick messages my JS FPS would drop to like 10, 5 even 1 and I found out today thanks to implementing a data/time display for the messages that each time I type something in the TextInput and run onChangeText event and change the state of "text" it re-renders ALL the items inside my FlatList
Here is my code:
Chat Component:
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
onChangedText Function
onFooterTextChanged = (text) => {
this.setState({
text: text,
options_expanded: false,
});
};
onSendMessage Button Function
onSendButtonPressed = () => {
if(this.state.text !== null && this.state.text !== "") {
this.setState({
items: [
...this.state.items,
{
id: moment().valueOf(),
text: this.state.text,
date: moment().valueOf(),
user: {
id: globals.user_id_value,
avatar: globals.user_photo,
}
}
],
text: "",
options_expanded: true,
});
}
};
renderItem Function for FlatList
renderItem = (item) => {
const data = item.item;
const renderAvatar = this.renderAvatar(item);
const renderTime = this.renderTime(item);
if('text' in data){
return(
<ChatTextItem
keyy={data.id}
self={globals.user_id_value === data.user.id}
text={data.text}
user={data.user}
renderAvatar={renderAvatar}
sameUser={!renderAvatar}
renderTime={renderTime}
time={this.getTime(data.date)}
/>
)
} else if('image' in data) {
return(
<ChatImageItem
keyy={data.id}
self={globals.user_id_value === data.user.id}
image={data.image}
renderAvatar={renderAvatar}
sameUser={!renderAvatar}
renderTime={renderTime}
time={this.getTime(data.date)}
/>
)
}
};
Constructor if it helps
constructor(props){
super(props);
this.state = {
isLoading: false,
options_expanded: true,
text: "",
image: "",
items: [],
};
}
and I'm user PureComponent btw.
Edit #1: Console after typing like a madman in the TextInput It re-rendered 28 times for the 28 letters I typed and it does that * the number of items in the list already
Edit #2: Changes Made to the JS file
Changed FlatList extraData option
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
and changed the constructor to add refresh state
constructor(props){
super(props);
this.state = {
isLoading: false,
options_expanded: true,
text: "",
image: "",
items: [],
refresh: false,
};
}
Issue still persists
Edit: #3 Finally found the issue
Works
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
Doesn't Work
<FlatList
key={'chat'}
ref={(ref) => {flatlistRef = ref}}
style={styles.flatlist_main}
data={this.state.items}
extraData={this.state.refresh}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onContentSizeChange={(contentWidth, contentHeight) => {
flatlistRef.scrollToEnd({animated: true});
}}
onLayout={() => {flatlistRef.scrollToEnd({animated: true})}}
/>
Any Ideas Why? Please!
If someone could help me find a solution to my problem I would appreciate it.
Share Improve this question edited May 22, 2018 at 13:45 Guy asked May 22, 2018 at 11:26 GuyGuy 1211 silver badge7 bronze badges 11-
in
FlatList
you re-render wheneverthis.state
changes withextraData={this.state}
andonTextChange
you updatethis.state
. That's why it's always re-renderFlatList
too. – gaback Commented May 22, 2018 at 12:43 -
I removed
extraData={this.state}
but continues to re-render everytime i type something – Guy Commented May 22, 2018 at 12:50 -
1
You should not remove
extraData
because if you remove it. TheFlatList
USUALLY won't re-render anymore. Do something like @soutot shows you on his answer. BTW, when you removeextraData
and it's still re-rendering. It means you are not running newest code or something totally wrong there. – gaback Commented May 22, 2018 at 12:59 - if you check again the post I updated it with the changes I made and the issue still persists, do you have any idea if its cache related? Thanks for your answer – Guy Commented May 22, 2018 at 13:07
- With information you have provided. I don't see anything wrong there. – gaback Commented May 22, 2018 at 13:14
5 Answers
Reset to default 3It looks like it's due to your extraData
receiving the whole state
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 ponent state
So this is the expected behavior since you are updating your state every time the user types in your input field.
Also, FlatList should render (or rerender) only the items visible in the screen
In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate and momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application, and we are working on improving it behind the scenes.
Try fixing your extraData
. Something like extraData={this.state.refresh}
where this.state.refresh
is updated when the user presses send
button.
More details here: https://facebook.github.io/react-native/docs/flatlist.html
Hope it helps
Apparently, if I remove onLayout
and onContentSizeChanged
events from my FlatList
it stops rendering on any state changes and only the state provided in extraData, still doesn't solve my issue and it only made performance worse surprisingly.
Thanks to everyone that helped to solve this.
@Guy hope you have found solution.
But for new devs. As per my struggle in same situation i found that:- For any use case when Flatlist and TextInput in same ponent then please try to use Flatlist in separate ponent (PureComponent) and textInput in separate ponent. Re-rendering will not happen.
Or you can use separate ponent for flatlist only, it will not re-render each time when state change.
It is due to the hooks of text input present in the same ponent containing flatlist.
Separate whole text input from the ponent
//Component contains flat list
const ParentComponent = ()=>{
return(
<FlatList/>
<CustomTextInput/>
);
}
//Textinput separate ponent
const CustomTextInput = ()=>{
const [value,setValue] = useState(null);
return (
<TextInput
value = {value}
onChangeText={(data)=>{setValue(data);}}
/>
);
}
I don't think it is bug, This is what we need it to your chat application right. As you were updating the state for each onPress(onSendButtonPressed) to show the new message in the list. So it re-renders the list. May be what i suggest is, you just show the recent messages in your flatlist. ie last 3 or 4 days message in the flatList and load more when the user goes backward beyond 4 days. This will help you to improve the performance of your chat application.