在 React Native 上获取异步 Firebase 数据
我正在使用 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 的数据?
尽管 OP 似乎已经找到了答案,但还是要回答这个问题,因为他还没有解释底层概念。
Firebase sdk 使用异步编程和观察者模式来提供实时更新。
在 React Native 世界中获取异步 Firebase 数据的正确方法如下。
-
在应用程序启动期间仅初始化一次 firebase sdk。用 React 术语来说,这可以在顶级 App 组件的构造函数中完成。 请参阅 firebase 文档了解步骤。 https://firebase.google.com/docs/database/web/start
-
在组件构造函数或 componentDidMount 中设置对 firebase 函数的调用以加载数据
componentDidMount(){ this.loadMessagesFromFirebase(this.state.chatId);
-
在加载消息函数中异步调用 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 的评论,我终于搞明白了。所有需要异步完成的操作都必须进入 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) 实际上会导致问题 - 它必须运行一次,并且要更早,例如在设置应用程序时)