Differentiate between the previous element and current element from the data of a Flatlist in react native

Viewed 3234

I am trying to build a chat app using react native. I am using flatlist to list out the messages of a conversation.

export default class ExampleConversation extends React.Component {
    _renderItem = ({item}) => {
        return (
            <View style={{backgroundColor: 'white', margin: 4, flexDirection: 'row'}}>
                <Text>{item.sender.id}</Text>
                <Text>{item.body}</Text>
            </View>
        )
    };

    render() {
        return <FlatList
                    data={data}
                    renderItem={this._renderItem}
                    keyExtractor={(item, id) => item.id}
                    style={styles.container}
                    inverted={true}/>
    }
}

JSON data fetched from the server:

const data = [
    {
        "id": 13,
        "sender_id": 1,
        "body": "Some message"
    },
    {
        "id": 12,
        "sender_id": 1,
        "body": "Some message"
    },
    {
        "id": 11,
        "sender_id": 5,
        "body": "Some message"
    }
];

As you can see from the above data, both first and second messages are from the same sender (sender_id: 1). When the previous message and the current message are from the same sender, I would like to remove the <Text>{sender_id}</Text> for that message object.

    return (
        <View style={{backgroundColor: 'white', margin: 4, flexDirection: 'row'}}>
            <Text>{item.body}</Text>
        </View>
    )

Basically, I would like to change the layout of the Message component by checking the elements given for the Flatlist data, much like how the facebook messenger displays its messages.

enter image description here

How can I differentiate between the previous and current elements by its key values of the Flatlist data?

3 Answers

Flatlist's renderItem prop provides an index property for the current rendering item. You can use index to find the previous item's sender is the same or not. To achieve this sort of behavior you need to have your data sorted.

For Example

const data = [
    {
        "id": 13,
        "sender_id": 1,
        "body": "Some message",
        "sentDate": 00003,
    },
    {
        "id": 12,
        "sender_id": 1,
        "body": "Some message"
        "sentDate": 00002,
    },
    {
        "id": 11,
        "sender_id": 5,
        "body": "Some message"
        "sentDate": 00001,
    }
];

    isSenderSame = ({currentMessage, prevMessage}) => {
      return currentMessage.sender_id === prevMessage.sender_id;
    }
    _renderItem = ({item, index}) => {
        const style = isSenderSame(item, this.state.data[(index - 1)]) ? styles.sameMessageStyle : styles.differentMessageStyle;
        return (
            <View style={[{backgroundColor: 'white', margin: 4, flexDirection: 'row'}, style]}>
                <Text>{item.sender.id}</Text>
                <Text>{item.body}</Text>
            </View>
        )
    };

    render() {
        return <FlatList
                    data={this.state.data}
                    renderItem={this._renderItem}
                    keyExtractor={(item, id) => item.id}
                    style={styles.container}
                    inverted={true}/>
    }

Update

Another approach would be let the server sort the data. You can send the messages in blocks and render them with a loop in renderItem

const data = [{
    "sender_id": 5,
    "messages": [{
        "id": 14,
        "body": "Some message"
        "sentDate": 00006
    }]
}, {
    "sender_id": 1,
    "messages": [{
        "id": 13,
        "body": "Some message",
        "sentDate": 00003
    }, {
        "id": 12,
        "body": "Some message"
        "sentDate": 00002,
    }]
}, {
    "sender_id": 5,
    "messages": [{
        "id": 11,
        "body": "Some message"
        "sentDate": 00001
    }]
}]

One approach can be like this, when you adding new object to data add its index too, something like,

let data = this.state.data;
let length = data.length;
this.setState({
  data:[...data, {...yourData, index:length+1}] //using ES6 spread operator
});

now inside your renderItem function try something like

let {index, id, sender_id} = item //using object destruction
let lastMessage = this.state.data[index-1];
let isSame = (sender_id === lastMessage. sender_id)?true:false; //use it 

***** there must be more performant approach than this.

Instead of having the logic in the render, you can also loop through it just once when you fetch the data then feed the result to your flatlist:

.then((data) => {
  const messages = data.map((item, index) => {
    if (index > 0) {
      return (item.sender_id === data[index].sender_id) ? { //return object without sender id } : item;
    }
  })
  this.setState({ data: messages });
})
Related