I'm making a chat application in React (Nextjs to be precise) and I've done all the components so far: the chat container, having a header, a messages list and an input. The code is listening for new messages, with each new message adding a new message child component in the messages list component.
This is a schematic version of the chat I want to achieve. Each message contains the text itself and the avatar of the person. The message data also contains the author, so using flex display, it places the message on one side or the other of the messages list. I would like the avatar to appear only on the last group of messages on each side.
I think that it should be MessageList
component responsible to know which message is the last one of each side every time, but I'm not sure how to achieve it. First I tried with CSS, but I can only display in the last message of each side (:last-child
), but not on the last one of each group, each side. Second, having an array marking each last message, but then I had to reorder and search for the previous one, and that lead to having avatars in the wrong messages. Also, having a state for each message doesn't seem optimal and efficient.
<ChatContainer>
<Header />
<MessagesList >
<Message author='me' />
<Message author='me' /> // <- avatar here
<Message author='them' />
<Message author='them' /> // <- avatar here
<Message author='me' />
<Message author='me' />
<Message author='me' /> // <- avatar here
<MessageList />
<Input />
<ChatContainer />
My question is, how I can efficiently and also in a "reactjs way" keep track of last message of each group of messages on each side?
CodePudding user response:
To display avatar in each group just check all messages and if current message author !== next message author then add avatar props to current
Also, having a state for each message doesn't seem optimal and efficient.
for this I need to see your code, you can create different question (for other people, when they will search smth like that) and paste link here
CodePudding user response:
Here's one solution worth trying:
Set up the type of chat message like this:
type ChatMessage = {
message: string,
author: string,
isLastOne: boolean // avatar only appears when this is true
}
const [messagesArr, setMessagesArr] = useState<ChatMessage[]>([]);
Suppose that, the me
and them
messages are stored in this single messagesArr
state.
There're two possible situations:
me
sends a message that appends to my own message, that push the avatar to the updated<Message author='me' />
(or vice versa);Before
me
sends a message to my message list,them
sends a message first, interrupting my own messages.
Code:
const initialData = [{message: "1", author: "me", isLastOne: true}]
const [messagesArr, setMessagesArr] = useState(initialData);
const newMessage = {message: "2", author: "me", isLastOne: true}; // newMessage.isLastOne always set to true
function pushNewMessage(messagesArr, newMessage) {
newState = [...messagesArr]; // make a copy
// Only when user is appending it's own message should we update the previous last element
const isUpdateOwnMessage = newState[newState.length - 1].author === newMessage.author;
if(isUpdateOwnMessage) {
newState[newState.length - 1].isLastOne = false; // update previous last element's isLastOne value
}
newState.push(newMessage);
return newState;
}
setMessagesArr(pushNewMessage(messagesArr, newMessageFromMe))
Different situations in action:
First situation: When appending to our own message, before we do that, we change the
previous last
element'sisLastOne
to false, and as new message always hasisLastOne
value set to true, this should render avatar correctly;Second situation: Message interrupted by
them
, we can dirrectly push to the list.
Since avatar's appearence is determined by both isLastOne
and author
value, then, we can safely make it appear on the end of author's message list, and by checking last element's owner before pushing, we can update the state correctly. Not sure if this makes sense.