为什么Firestore的'doc.get('time').toMillis'会产生空类型错误? [英] Why is Firestore's 'doc.get('time').toMillis' producing a null Type Error?

查看:40
本文介绍了为什么Firestore的'doc.get('time').toMillis'会产生空类型错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个本机应用程序中,我称一个将数据发送到Firebase的动作.但是在通话之后,我收到一个错误,该错误的来源似乎与快照侦听器的工作方式有关.add-msg操作或多或少看起来像这样:

In a react-native application, I call an action sends data to firebase. but after the call, I get an error whose source seems to be with the way the snapshot listener is working. the add-msg action more or less looks like this:

创建数据:

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存储.快照侦听器如下所示:

I also have a snapshot listener (that executes in componentDidMount) that populates a redux store with messages from a collection in firestore. the snapshot listener looks like:

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();
                });
        });
    };
};

在同一屏幕组件中填充flatList的相应的reducer如下所示:

the corresponding reducer that populates a flatList in the same screen component which looks like:

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'如果我重新加载该应用程序,然后导航回到有问题的屏幕,则出现味精,时间数据也将出现.时间值的初始化,但是它足以使应用程序崩溃.

Problem: once the data sends, I immediately get an error TypeError: null is not an object (evaluating 'doc.get('time').toMillis'. if I reload the application, navigate back to the screen in question, the msg appears, and so does the time data. So my guess is something is happening with the delay in the promissory nature of firebase calls, and the delay causes a null initialization of the time value, but its long enough to crash the application.

问题:这里的幕后实际上发生了什么,如何防止该错误?

Question: what is actually happening behind the scenes here and how can I prevent this error?

推荐答案

您遇到的问题是由于 onSnapshot()侦听器在一个小窗口中从本地Firestore缓存触发而引起的 firebase.firestore.FieldValue.serverTimestamp() 被视为待处理,默认情况下被视为 null .服务器接受您的更改后,它将以所有新的时间戳记值作为响应,并再次触发您的 onSnapshot()侦听器.

The problem you encounter is caused by the onSnapshot() listener firing from the local Firestore cache during a small window where the value of firebase.firestore.FieldValue.serverTimestamp() is considered pending and is treated as null by default. Once the server accepts your changes, it responds with all the new values for the timestamps and triggers your onSnapshot() listener again.

如果不加注意,这可能会导致您的应用闪烁",因为它将数据两次打印掉了.

Without care, this may cause your app to 'flicker' as it stamps the data out twice.

要更改待处理时间戳的行为,您可以传递 SnapshotOptions 对象作为 ="nofollow noreferrer"> doc.get() .

To change the behaviour of pending timestamps, you can pass a SnapshotOptions object as the last argument to doc.data() and doc.get() as appropriate.

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

The following code, instructs the Firebase SDK to estimate the new timestamp values based on the local clock.

const estimateTimestamps = {
  serverTimestamps: 'estimate'
}

querySnapshot.forEach((doc) => {
  const msg = doc.data(); // here msg.time = null
  msg.docId = doc.id;
  msg.time = doc.get('time', estimateTimestamps).toMillis(); // update msg.time to set value (or estimate if not available)

  messages.push(msg);
});

如果要显示消息仍在写入数据库,可以在估计时间戳之前检查 msg.time 是否为 null .

If you want to show that your message is still being written to the database, you could check if msg.time is null just before estimating the timestamp.

const estimateTimestamps = {
  serverTimestamps: 'estimate'
}

querySnapshot.forEach((doc) => {
  const msg = doc.data(); // here msg.time = null when pending
  msg.docId = doc.id;
  msg.isPending = msg.time === null;
  msg.time = doc.get('time', estimateTimestamps).toMillis(); // update msg.time to set value (or estimate if not available)

  messages.push(msg);
});

如果您希望忽略这些中间的本地"事件,而等待服务器的完整响应,则可以使用:

If you want to ignore these intermediate 'local' events in favour of waiting for the server's full response, you would use:

.onSnapshot({includeMetadataChanges: true}, (querySnapshot) => {
  if (querySnapshot.metadata.fromCache && querySnapshot.metadata.hasPendingWrites) {
    return; // ignore cache snapshots where new data is being written
  }
  const messages = [];
  querySnapshot.forEach((doc) => {
    const msg = doc.data();
    msg.docId = doc.id;
    msg.time = doc.get('time', estimateTimestamps).toMillis();

    messages.push(msg);
  });
  dispatch({ type: types.LOAD_MSGS, payload: messages });
  resolve();
});

在上面的代码块中,请注意,我还检查了 querySnapshot.metadata.hasPendingWrites ,然后忽略该事件,以便在您首次加载应用程序时,它将立即打印出所有缓存的信息.否则,您将显示一个空消息列表,直到服务器响应为止.大多数站点将打印出所有缓存的数据,同时在页面顶部显示一个throbber,直到服务器响应任何新数据为止.

In the above code block, note that I also checked querySnapshot.metadata.hasPendingWrites before ignoring the event so that when you first load up your app, it will print out any cached information immediately. Without this, you will show an empty message list until the server responds. Most sites will print out any cached data while showing a throbber at the top of the page until the server responds with any new data.

这篇关于为什么Firestore的'doc.get('time').toMillis'会产生空类型错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆