我想将其转换为useEffect
钩子:
代码
componentDidMount () {
this.messagesRef.on('child_added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
this.setState({messages: this.state.messages.concat(message
)});
});
更新代码
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)});
});
})
}
现在,它给了我useState cannot be used in a callback
。
我该如何解决或正确转换?
答案 0 :(得分:2)
那里有几件事。首先,要修复代码,您可以将useEffect
更新为此:
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
setMessages(messages.concat(message)); // See Note 1
}, []); // See Note 2
setMessages
行是您更新状态的方式。 useState
与“旧” setState
有点不同,它将完全替换状态值。反应documentation说:
这是因为更新状态变量时会替换其值。这与类中的this.setState不同,该类将已更新的字段合并到对象中。
React Hooks改变了我们构建应用程序的方式,它并不是旧生命周期的真正“翻译”。
最后一行的方括号([]
)将使您的代码与componentDidMount
“相似”,但最重要的是,您的效果只能运行一次。
丹·阿布拉莫夫说(删除了一些原始文本):
虽然可以使用Effect(fn,[]),但它并不完全等效。与componentDidMount不同,它将捕获道具和状态。因此,即使在回调函数内部,您也会看到初始的道具和状态。 (...)请记住,效果的心理模型不同于componentDidMount和其他生命周期,尝试找到它们的等效模型可能会使您困惑甚于帮助。为了提高工作效率,您需要“思考效果”,并且他们的思维模型更接近于实现同步,而不是响应生命周期事件。
有关useEffect here的全文。
答案 1 :(得分:1)
您尝试再次声明状态,而不是使用状态更新程序
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
}, []);
答案 2 :(得分:1)
我找到了一个不需要修改 html 的替代解决方案。我们创建了一个高阶组件,显示一些等待元素并在 componentDidMount 中切换状态或使用 effect 来渲染目标组件。
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;
缺点是动画元素不起作用。