React-useEffect钩子-要使用useEffect的componentDidMount

时间:2019-05-22 04:05:17

标签: reactjs use-effect

我想将其转换为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

我该如何解决或正确转换?

3 个答案:

答案 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

注释1

setMessages行是您更新状态的方式。 useState与“旧” setState有点不同,它将完全替换状态值。反应documentation说:

  

这是因为更新状态变量时会替换其值。这与类中的this.setState不同,该类将已更新的字段合并到对象中。

注释2

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;

缺点是动画元素不起作用。