开发者问题收集

在 React Native 上获取异步 Firebase 数据

2021-02-12
768

我正在使用 Firebase 构建 React Native 应用。我在我的一个组件中使用以下方法(以及其他方法)从 Firebase 获取数据:

loadMessagesFromFirebase(chatId){
    let messageList = [];
    let data = {};
    let message = {};
    const dataRef = firebase.database().ref('chats').child(chatId);
    dataRef.on('value', datasnap=>{
        data = datasnap.val()
    })
    for (var sayer in data){
        for (var m in data[sayer]){
            message = {sayer: sayer, text: m.text, time: new Date(m.created)};
            messageList.push(message);
        }
    }
    messageList.sort((a,b) => (a.time > b.time) ? 1: -1);
    this.setState({messageList: messageList});
  }

问题是,有时数据会作为空字典 (Object {}) 加载,因此消息列表将为空。我假设发生这种情况是因为我没有给 Firebase 足够的加载时间。如何使此功能异步,以便我可以向组件添加“加载”状态,并且在加载完成之前不显示消息信息?

async componentDidMount(){
    firebase.initializeApp(FirebaseConfig);
    this.loadMessagesFromFirebase(this.state.chatId);
    //once the messages are done loading
    this.setState({{loading: false}})
}

此外,有没有办法确保,如果数据作为空字典返回,那是因为 Firebase 已完成加载,而不是因为没有此聊天 ID 的数据?

2个回答

尽管 OP 似乎已经找到了答案,但还是要回答这个问题,因为他还没有解释底层概念。

Firebase sdk 使用异步编程和观察者模式来提供实时更新。

在 React Native 世界中获取异步 Firebase 数据的正确方法如下。

  1. 在应用程序启动期间仅初始化一次 firebase sdk。用 React 术语来说,这可以在顶级 App 组件的构造函数中完成。 请参阅 firebase 文档了解步骤。 https://firebase.google.com/docs/database/web/start

  2. 在组件构造函数或 componentDidMount 中设置对 firebase 函数的调用以加载数据 componentDidMount(){ this.loadMessagesFromFirebase(this.state.chatId);

  3. 在加载消息函数中异步调用 firebase realtor 数据库。更多阅读请见此处 https://firebase.google.com/docs/database/web/read-and-write

这里要记住的主要一点是,所有在数据可用后必须运行的代码都必须通过编写异步回调来触发。修改问题中的示例代码

loadMessagesFromFirebase(chatId){
    let data = {};
    let output = {};
    const dataRef = firebase.database().ref('chats').child(chatId);
    dataRef.once('value', datasnap=>{
        data = datasnap.val();

        // Do something really smart with the data and assign it to output
        ...... 
        output = data;
        // set updates to the state
        this.setState({output: output});
    })
  }

请注意使用 once 而不是 on 函数。 这样做的原因是,为了获得对观察者的订阅, on 可能导致每次数据发生变化时都会触发回调。如果组件设计为仅获取一次数据,则可能会导致不良后果。

进一步阅读 https://firebase.google.com/docs/database/web/read-and-write

如果希望每次数据发生变化时更新组件,则使用 on 函数设置对该数据的订阅。但是在这种情况下,取消 componentWillUnmount 内的订阅非常重要。>

https://firebase.google.com/docs/reference/js/firebase.database.Reference#off

可以总结如下

`
// inside componentDidMount 
this.onValueChange = ref.on('value', function(dataSnapshot) { ... });


// Sometime later.... Inside componentWillUnmount
ref.off('value', this.onValueChange);`
vvs
2021-02-14

感谢 vvs 的评论,我终于搞明白了。所有需要异步完成的操作都必须进入 dataref.on() 函数。

loadMessagesFromFirebase(chatId){
    let messageList = [];
    let data = {};
    let message = {};
    const dataRef = firebase.database().ref('chats').child(chatId);
    dataRef.on('value', datasnap=>{
        data = datasnap.val()
        for (var sayer in data){
            for (var m in data[sayer]){
                message = {sayer: sayer, text: m.text, time: new Date(m.created)};
                messageList.push(message);
            }
        }
        messageList.sort((a,b) => (a.time > b.time) ? 1: -1);
        this.setState({messageList: messageList});
        this.setState({{loading: false}})
    })
  }

该函数的调用方式如下:

componentDidMount(){
    this.loadMessagesFromFirebase(this.state.chatId);
}

(在其中使用 firebase.initializeapp(FirebaseConfig) 实际上会导致问题 - 它必须运行一次,并且要更早,例如在设置应用程序时)

martincito
2021-02-13