I would like to convert this to an useEffect
hook:
CODE
componentDidMount () {
this.messagesRef.on('child_added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
this.setState({messages: this.state.messages.concat(message
)});
});
UPDATED CODE
const MessageList = () => {
const [messages, setMessage] = useState([]);
const [usernames, setUsernames] = useState('');
const [contents, setContents] = useState('');
const [roomId, setRoomId] = useState('');
const messagesRef = MessageList.props.firebase.database().ref('messages');
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
const [messages, setMessages] = useState({messages: messages.concat(message)});
});
})
}
Right now it's giving me a useState cannot be used in a callback
.
How can I address this or convert this properly?
The equivalent of componentDidMount in hooks is the useEffect function. Functions passed to useEffect are executed on every component rendering—unless you pass a second argument to it.
Hooks and useEffect() both run after the component is mounted. The difference is that hooks are also run after the DOM content has been painted. So, if the state is updated synchronously within an effect method users will see a flicker as the first frame is replaced with the second frame.
There are a couple of things there. First, to fix the code, you could update your useEffect
to this:
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
setMessages(messages.concat(message)); // See Note 1
}, []); // See Note 2
The setMessages
line is how you update your state. useState
is a little bit different from the "old" setState
in a sense that will completely replace the state value. React documentation says:
This is because when we update a state variable, we replace its value. This is different from this.setState in a class, which merges the updated fields into the object.
React Hooks changes the way we build apps and it is not a real "translation" from the old lifecycles.
The empty brackets ([]
) in the last line, will make your code "similar" to componentDidMount
, but most importantly, will make your effect run only once.
Dan Abramov said (removed some of the original text):
While you can useEffect(fn, []), it’s not an exact equivalent. Unlike componentDidMount, it will capture props and state. So even inside the callbacks, you’ll see the initial props and state. (...) Keep in mind that the mental model for effects is different from componentDidMount and other lifecycles, and trying to find their exact equivalents may confuse you more than help. To get productive, you need to “think in effects”, and their mental model is closer to implementing synchronization than to responding to lifecycle events.
Full article about useEffect here.
You tried to declare the state again instead of using the state updater
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
// setMessages is the state updater for messages
// instead of an object with messages: messagesArray
// just save it as an array the name is already messages
setMessages([...messages, message]);
});
// useEffect takes an array as second argument with the dependencies
// of the effect, if one of the dependencies changes the effect will rerun
// provide an empty array if you want to run this effect only on mount
}, []);
I found an alternative solution that doesn't require modifying html. We create a higher order component that displays some waiting element and switch the state in componentDidMount or use effect in order to render the target component.
import React, { useEffect, useState } from 'react';
const Loading = (props) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
return (
<>
{loading ?
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: '100%',
fontSize: '5vh'
}}>
Loading...
</div> :
props.children}
</>
);
};
export default Loading;
The disadvantage is that the animated elements are not working.
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