最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - FlatList re-rendering every time i type something in TextInput - Stack Overflow

programmeradmin0浏览0评论

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 whenever this.state changes with extraData={this.state} and onTextChange you update this.state. That's why it's always re-render FlatList 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. The FlatList USUALLY won't re-render anymore. Do something like @soutot shows you on his answer. BTW, when you remove extraData 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
 |  Show 6 more ments

5 Answers 5

Reset to default 3

It 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.

发布评论

评论列表(0)

  1. 暂无评论