Reputation: 147
I am building a small chat app which has the following HTML structure.
<div className="my-message">
<div className="mess">
<p className="my-message">Hello</p>
</div>
<div className="mess">
<p className="my-message">How are you</p>
</div>
<p className="time-i-sent">1m ago</p>
</div>
<div className="my-message other-message">
<div className="mess">
<p className="my-message other-message-2">Hi</p>
</div>
<div className="mess">
<p className="my-message other-message-2">Great</p>
</div>
<p className="time-i-sent other-message-3">5m ago</p>
</div>
You can notice that messages in a row that are from the same user are just divs with mess
class. If other user responds, a new my-message
div wraps the messages that the user will send consecutively. I reflect them as React components.
Here is the Messages
component equivalent to my-message
div that wraps consecutive messages.
const Messages = (props: any) => {
const isOtherUser = props.userId !== props.currentUser.id;
return (
<div
className={`my-message ${isOtherUser ? "other-message" : ""}`}
>
{props.children}
{props.lastMessage && (
<p className={`time-i-sent ${isOtherUser ? "other-message-3" : ""}`}>
{getTimeSince(props.time)}
</p>
)}
</div>
);
};
And here is my Message
component which should be wrapped by Messages
component if they are from the same user consecutively.
const Message = (props: any) => (
<div className="mess">
{props.userId === props.currentUser.id ? (
<p className="my-message">{props.text}</p>
) : (
<p className="my-message other-message-2">{props.text}</p>
)}
</div>
);
And here is what I try to do
messages.map((message, index) => {
if (index > 0 && messages[index - 1].userId === message.userId) {
return (
<Messages
{...message}
currentUser={currentUser}
lastMessage={index === messages.length - 1}
>
<Message key={index} {...message} currentUser={currentUser} />
</Messages>
);
} else {
return (
<Message key={index} {...message} currentUser={currentUser} />
);
}
})
It looks strange, but I want to check if the previous message was from the same user, just make a Message
component and put it inside the current Messages
component, if messages are from different users, make a new Messages
component and add Message
components inside it in case user sent multiple messages in a row so that in the end I have something like the following.
<Messages>
<Message>Hi</Message>
<Message>How are you</Message>
</Messages>
<Messages>
<Message>Hi</Message>
<Message>Great, you?</Message>
</Messages>
<Messages>
<Message>Awesome</Message>
</Messages>
<Messages>
<Message>Bye</Message>
</Messages>
I think this should be the logic but not sure how to achieve appending the components here I thought of HOC as well.
Upvotes: 0
Views: 1043
Reputation: 3070
As you want to reduce the number of elements of messages
grouping the ones with the same userId
, you can try using Array.reduce
:
const groups = messages.reduce((acc, message, index) => {
if (index > 0 && messages[index - 1].userId === message.userId) {
// Same group as before
acc[ acc.length-1 ].push({message, index});
} else {
// New group
acc.push([{message, index}])
}
}, []);
return groups.map( (group, groupIndex) => (
<Messages
key={groupIndex}
{...group[group.length-1].message}
currentUser={currentUser}
lastMessage={groupIndex === group.length - 1}
>
{ group.map( ({message, index}) => (
<Message key={index} {...message} currentUser={currentUser} />
))
}
</Messages>
)
);
But I think maybe it would be better to modify the Messages
component, so it accepts an array of messages instead of using children
.
Upvotes: 1