The code below works fine by displaying users info in a popup box. Now I want to send message to each user to a nodejs server. The message got posted as I can see it in the nodejs server end console.
In reactjs console, as per code below
const addMessage = data => {
console.log(data);
this.setState({messages: [...this.state.messages, data]});
};
am seeing the message sent in the console as per
{author: "Nancy ", message: "first message"}
Here is my issue:
1.) The message is not displayed within the render method despite the fact that I have set it to this.props.messages.. 2.) When I typed on the input form am seeing error warning in the console as per below
Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. in input (created by OpenedUser)
Below is how am posting and retrieving it from nodejs server
componentDidMount(){
this.socket = io('http://localhost:8080');
this.socket.on('response message', function(data){
addMessage(data);
});
const addMessage = data => {
console.log(data);
this.setState({messages: [...this.state.messages, data]});
};
} // close component didmount
sendMessage = ev => {
ev.preventDefault();
this.socket.emit('chatMessage', {
author: this.state.username,
message: this.state.message
})
this.setState({message: ''});
}
Here is the full working code so far
//npm install --save socket.io
//npm install --save socket.io-client
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import { Link } from 'react-router-dom';
import axios from 'axios';
import io from "socket.io-client";
class User extends React.Component {
open = () => this.props.open(this.props.data.id, this.props.data.name);
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button
onClick={() => this.open(this.props.data.id, this.props.data.name)}
>
{this.props.data.name}
</button>
</div>
</React.Fragment>
);
}
}
class OpenedUser extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: false
};
}
componentDidMount(){
this.socket = io('http://localhost:8080');
this.socket.on('response message', function(data){
addMessage(data);
});
const addMessage = data => {
console.log(data);
this.setState({messages: [...this.state.messages, data]});
};
} // close component didmount
sendMessage = ev => {
ev.preventDefault();
this.socket.emit('chatMessage', {
author: this.state.username,
message: this.state.message
})
this.setState({message: ''});
}
toggleHidden = () => this.setState(prevState => ({ hidden: !prevState.hidden }));
close = () => this.props.close(this.props.data.id);
render() {
return (
<div key={this.props.data.id} style={{ display: "inline-block" }}>
<div className="msg_head">
<button onClick={this.close}>close</button>
<div>user {this.props.data.id}</div>
<div>name {this.props.data.name}</div>
{this.state.hidden ? null : (
<div className="msg_wrap">
<div className="msg_body">Message will appear here</div>
<b> {" "} Display Chat Message below...{" "}</b>
<div>
{this.props.messages.map(message => {
return (
<div>{message.author}: {message.message}</div>
)
})}
</div>
<div>
<input type="text" placeholder="Username" value={this.state.username} onChange={ev => this.setState({username: ev.target.value})}/>
<br/>
<input type="text" placeholder="Message" value={this.state.message} onChange={ev => this.setState({message: ev.target.value})}/>
<br/>
<button onClick={this.sendMessage} className="btn btn-primary">Send message</button>
</div>
</div>
)}
</div>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
loading_image: false,
shown: true,
activeIds: [],
username: '',
message: '',
messages: [],
data: [
{ id: 1, name: "user 1" },
{ id: 2, name: "user 2" },
{ id: 3, name: "user 3" },
{ id: 4, name: "user 4" },
{ id: 5, name: "user 5" }
]
};
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
open = (id, name) => {
alert(id);
alert(name);
//start axios api call
const user_data = {
uid: "id",
uname: "name"
};
this.setState(prevState => ({
activeIds: prevState.activeIds.find(user => user === id)
? prevState.activeIds
: [...prevState.activeIds, id]
}));
};
close = id => {
this.setState(prevState => ({
activeIds: prevState.activeIds.filter(user => user !== id)
}));
};
renderUser = id => {
const user = this.state.data.find(user => user.id === id);
if (!user) {
return null;
}
return (
<OpenedUser
chatData={this.state.chatData} messages={this.state.messages}
key={user.id}
data={user}
close={this.close}
/>
);
};
renderActiveUser = () => {
return (
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{this.state.activeIds.map(id => this.renderUser(id))}
</div>
);
};
render() {
return (
<div>
{this.state.data.map(person => (
<User key={person.id} data={person} open={this.open} />
))}
{this.state.activeIds.length !== 0 && this.renderActiveUser()}
</div>
);
}
}
Within OpenedUser.sendMessage, you have following code to associate input values to this.state.username/message respectively.
// Reformatted to remove scrollbars
<input type="text" placeholder="Username"
value={this.state.username}
onChange={ev => this.setState({username: ev.target.value})}/>
<br />
<input type="text" placeholder="Message"
value={this.state.message}
onChange={ev => this.setState({message: ev.target.value})}/>
Initially, your this.state.username/message are undefined, thus those two components are thought to be uncontrolled by React.
Refer to following docs for
Once your set values for those states in onChange, React now thinks that you are changing the controlled component values as your error message showed.
Warning: A component is changing an uncontrolled input of type text to be controlled...
So what you need to do is to declare those two state values in OpenedUser with the rest of existing state, hidden.
class OpenedUser extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: false,
// empty/props or other sensible default value
message: "",
// empty/props or other sensible default value
username: ""
};
}
// omitted for brevity...
}
I am sorry 😅 I haven't been able to test the code above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With