I’m develepoing a chat page with a React class component. The code should append every message that I sent by the input to <MessageGroup sentByUser>
block
class ChatPage extends Component {
constructor(props) {
super(props);
this.state = {
userMessages: [[0, 'First example message'], [1, 'Second msg']],
userMessageNextId: 2,
};
this.sendMessage = this.sendMessage.bind(this);
}
render() {
console.log(this.state.userMessages);
return (
<div className="chat-page mb-5">
<div className="messages-container">
<MessageGroup sentBy="Juan">
<Message>This is first message</Message>
<Message>This is second message</Message>
</MessageGroup>
<MessageGroup sentByUser>
{this.state.userMessages.map((val) => (<Message key={val[0]}>{val[1]}</Message>))}
</MessageGroup>
</div>
<div className="input-container">
<div className="input-group">
<input type="text" className="form-control" id="message-text" placeholder="Type something..."/>
<button className="btn btn-primary" onClick={this.sendMessage}>Send</button>
</div>
</div>
</div>
)
}
sendMessage() {
const messageText = document.querySelector("#message-text").value;
if (!messageText?.trim()) return;
this.setState(state => {
console.log('Setting state...')
const newArray = [...state.userMessages, [state.userMessageNextId, messageText]];
return {
userMessages: newArray,
userMessageNextId: state.userMessageNextId + 1
}
})
}
}
When I’m debugging my code, I see two default messages from the state. And when I send a message, I see "Setting state…" and new needed array in the console, but the UI is not changed. What’s wrong here?
2
Answers
You should use controlled components in React to manage the input value instead of
document.querySelector
.The main issue here is your use of a native DOM method to interrogate the DOM to grab the input value when really you should be keeping that value in state, and using a method to update that value whenever the input
onChange
event is fired. Then, when the button is clicked, you can then copy thatinput
state value into theuserMessages
state.(Using
value={this.state.input}
makes the input a controlled input – changes in state are reflected back to its value when the component re-renders. It makes sure everything is sync’d, and is a more "React way" of approaching the problem.)Further: you’ll probably find an array of objects a little more easy to work with. Property names are far more meaningful than index values. For example you can set an
id
property which can serve is both the unique object identifier, and thekey
for the iterated messages.Obviously it’s ultimately your choice but I decided to use them in this example to show them working.