Reputation: 43
class App extends Component {
constructor(props) {
super(props)
this.state = { text: "", messages: [] }
}
componentDidMount() {
const config = {
apiKey: "<api-key>",
authDomain: "<project-name>.firebaseapp.com",
databaseURL: "https://<project-name>.firebaseio.com",
projectId: "<project-name>",
storageBucket: "<project-name>.appspot.com",
messagingSenderId: "<sender-id>",
};
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
this.getMessages()
var database = firebase.database();
var ref = database.ref('messages');
}
onSubmit = event => {
if (event.charCode === 13 && this.state.text.trim() !== "") {
this.writeMessageToDB(this.state.text)
this.setState({ text: "" })
}
}
writeMessageToDB = () => {
firebase.database().ref('messages/').push({
text: this.state.text,
createdAt: createdAt,
user:{
_id: currentUser,
name:name
}
});
}
getMessages = () => {
var messagesDB = firebase
.database()
.ref("messages/")
.limitToLast(500)
messagesDB.on("value", snapshot => {
let newMessages = []
snapshot.forEach(child => {
var message = child.val()
var yeah = dateFormat(message.createdAt,"dddd, mmmm dS, yyyy, h:MM:ss TT")
newMessages.push({ id: child.key, text: message.text,createdAt: yeah, names: message.name })
})
this.setState({ messages: newMessages })
this.bottomSpan.scrollIntoView({ behavior: "smooth" })
})
}
renderMessages = () => {
return this.state.messages.map(message => (
<ListItem>
<ListItemText className="chatList"
style={{ wordBreak: "break-word", backgroundColor: "#FFA1B5", borderRadius: "10px", width: "10px", padding: "5px" }}
primary={message.name+": "+message.text}
secondary={message.createdAt}
/>
</ListItem>
))
}
render() {
return (
<MuiThemeProvider theme={theme}>
<div style={mainCont}>
<label style={labelStyle} className="labelStyle"> Chat</label>
<div className="App" >
<ScrollToBottom className={ ROOT_CSS }>
<List>{this.renderMessages()}</List>
</ScrollToBottom>
<TextField className="txtFieldStyle"
autoFocus={true}
multiline={true}
rowsMax={3}
placeholder="Type something.."
onChange={event => this.setState({ text: event.target.value })}
value={this.state.text}
onKeyPress={this.onSubmit}
style={{ width: "350px", overflow: "hidden", marginLeft: "15px", fontSize: '63px', paddingBottom: '5px' }}
/>
<span ref={el => (this.bottomSpan = el)} />
</div>
</div>
</MuiThemeProvider>
)
}
}
export default App;
chat feature is working fine unless the user navigates to other pages and go back to the chat feature and attempts to send message through chat.
Upvotes: 1
Views: 5520
Reputation: 38992
The following two lines in App.componentDidMount()
are a potential race condition.
this.setState({ messages: newMessages })
this.bottomSpan.scrollIntoView({ behavior: "smooth" })
The App
state may be mutated earlier to begin a render cycle so that the bottomSpan
is set to reference the element before this.bottomSpan.scrollIntoView()
is called.
However, this.bottomSpan.scrollIntoView()
is never guaranteed to be called after state is updated.
Remember that setState
doesn't always immediately mutate the state of the component. [1]
You can scroll the referenced element into view after state is mutated by doing that in a callback passed as the second argument to state.
this.setState(
{ messages: newMessages },
() => this.bottomSpan.scrollIntoView({ behavior: "smooth" })
)
Upvotes: 2