Javascript 为什么Firestore';s';doc.get(';time';).toMillis';是否生成空类型错误?

Javascript 为什么Firestore';s';doc.get(';time';).toMillis';是否生成空类型错误?,javascript,firebase,react-native,redux,react-redux,Javascript,Firebase,React Native,Redux,React Redux,在react本机应用程序中,我调用一个动作,将数据发送到firebase。但是在调用之后,我得到一个错误,其来源似乎与快照侦听器的工作方式有关。添加消息操作大致如下所示: 创建数据: const addMsg = (msg, convoIds) => { console.log('Firestore Write: (actions/conversation) => addMsg()'); return firebase.firestore().collection(

在react本机应用程序中,我调用一个动作,将数据发送到firebase。但是在调用之后,我得到一个错误,其来源似乎与快照侦听器的工作方式有关。添加消息操作大致如下所示:

创建数据:

const addMsg = (msg, convoIds) => {
    console.log('Firestore Write: (actions/conversation) => addMsg()');

    return firebase.firestore().collection('messages').add({
        time: firebase.firestore.FieldValue.serverTimestamp(),
        sender: msg.sender,
        receiverToken: msg.receiverToken,
        sessionId: msg.sessionId,
        read: false,
        charged: false,
        userConvos: [ convoIds.sender, convoIds.receiver ],
        content: {
            type: 'msg',
            data: msg.text
        }
    });
};
我还有一个快照侦听器(在componentDidMount中执行),它用firestore中的集合中的消息填充redux存储。快照侦听器如下所示:

export const getMessages = (convoId) => {
    const tmp = convoId == null ? '' : convoId;
    return (dispatch) => {
        console.log('Firestore Read (Listener): (actions/conversation) => getMessages()');
        return new Promise((resolve, reject) => {
            firebase
                .firestore()
                .collection('messages')
                .where('userConvos', 'array-contains', tmp)
                .orderBy('time')
                .onSnapshot((querySnapshot) => {
                    const messages = [];
                    querySnapshot.forEach((doc) => {
                        const msg = doc.data();
                        msg.docId = doc.id;
                        msg.time = doc.get('time').toMillis();

                        messages.push(msg);
                    });
                    dispatch({ type: types.LOAD_MSGS, payload: messages });
                    resolve();
                });
        });
    };
};
在同一屏幕组件中填充平面列表的相应缩减器,如下所示:

const INITIAL_STATE = {
    messages: []
};
export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case types.LOAD_MSGS:
            return {
                messages: action.payload
            };
        default:
            return { ...state };
    }
};
问题:一旦数据发送,我立即得到一个错误
TypeError:null不是一个对象(评估'doc.get('time')).toMillis'
。如果我重新加载应用程序,导航回相关屏幕,会显示消息,时间数据也会显示。因此,我猜firebase调用的约定性质中出现了延迟,延迟会导致时间值初始化为空,但其长到足以使应用程序崩溃


问题:这里的幕后到底发生了什么?我如何防止此错误?

您遇到的问题是由
onSnapshot()引起的
侦听器在一个小窗口期间从本地Firestore缓存触发,其中的值被认为是挂起的,默认情况下被视为
null
。一旦服务器接受您的更改,它将使用时间戳的所有新值进行响应,并再次触发您的
onSnapshot()
侦听器

如果不小心,这可能会导致你的应用程序“闪烁”,因为它会将数据打印两次

要更改挂起的时间戳的行为,可以将对象作为最后一个参数传递给和(视情况而定)

以下代码指示Firebase SDK根据本地时钟估计新的时间戳值

const estimateTimestamps={
服务器时间戳:“估计”
}
querySnapshot.forEach((doc)=>{
const msg=doc.data();//此处msg.time=null
msg.docId=doc.id;
msg.time=doc.get('time',estimateTimestamps).toMillis();//更新msg.time以设置值(或估计值,如果不可用)
消息推送(msg);
});
如果要显示消息仍在写入数据库,可以在估计时间戳之前检查
msg.time
是否为
null

const estimateTimestamps={
服务器时间戳:“估计”
}
querySnapshot.forEach((doc)=>{
const msg=doc.data();//挂起时此处msg.time=null
msg.docId=doc.id;
msg.isPending=msg.time==null;
msg.time=doc.get('time',estimateTimestamps).toMillis();//更新msg.time以设置值(或估计值,如果不可用)
消息推送(msg);
});
如果您希望忽略这些中间“本地”事件而等待服务器的完整响应,您可以使用:

.onSnapshot({includeMetadataChanges:true},(querySnapshot)=>{
if(querySnapshot.metadata.fromCache&&querySnapshot.metadata.hasPendingWrites){
return;//忽略正在写入新数据的缓存快照
}
常量消息=[];
querySnapshot.forEach((doc)=>{
const msg=doc.data();
msg.docId=doc.id;
msg.time=doc.get('time',estimateTimestamps).toMillis();
消息推送(msg);
});
分派({type:types.LOAD_MSGS,payload:messages});
解决();
});

在上面的代码块中,请注意,我在忽略事件之前也进行了检查,这样当您第一次加载应用程序时,它会立即打印出所有缓存的信息。如果不这样做,您将显示一个空消息列表,直到服务器响应。大多数站点将打印出所有缓存的数据,同时在页面顶部显示一个跳动器,直到服务器会以任何新数据进行响应。

doc.get('time')null吗?@LajosArpad当我在屏幕组件中访问它时,它会显示时间如果我是你,我会在
msg.time=doc.get('time').toMillis()行之前;
console.log(doc.get('time').toMillis()的形式做一个实验)
。您将看到这是否为空。这是您解决问题所需的关键信息。@LajosArpad已经有了。所有时间都是get-loggedCan您可以添加一个关于您遇到问题的确切错误消息的逐字引用吗?作为补充说明,别忘了正确处理您的侦听器to
onSnapshot()
返回一个取消订阅函数,当您的组件被释放时,您应该存储并调用该函数。例如,
const UnsbMessageListener=firebase…onSnapshot(…);
和当被释放时调用
UnsbMessageListener()
。非常有见地的信息。但是在尝试列出的所有三个选项后,我仍然遇到相同的错误,听起来像是您的集合中的一个或多个文档缺少
时间
字段/将其设置为空值,这可能是由于将
消息
数组中的一个条目保存回数据库而没有t正确的时间戳(因此,
msg.time
现在应该是
null
),例如在编辑消息时。请尝试此操作,而不是
msg.time=…
行:
constamp timestamp=doc.get('time',estimateTimestamps);if(timestamp){msg.time=timestamp.toMillis();}else{console.error(doc id+'缺少“time”字段!');/*debugger;*/}
。如果遇到问题,请取消注释
debugger;
并仔细查看。是的,执行了else条件,但当我检查相关文档时,“时间”字段在那里?我将奖励赏金,因为您是唯一花时间帮助的人,尽管我尚未修复错误:'(