gpt4 book ai didi

javascript - 为什么 Firestore 的 'doc.get(' time').toMillis' 会产生空类型错误?

转载 作者:行者123 更新时间:2023-12-01 16:18:08 26 4
gpt4 key购买 nike

在 react-native 应用程序中,我调用一个 Action 将数据发送到 firebase。但在调用之后,我收到一个错误,其来源似乎与快照监听器的工作方式有关。 add-msg 操作大致如下所示:

创建数据:

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

在同一屏幕组件中填充 flatList 的相应缩减器,如下所示:

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 is not an object (evaluating 'doc.get('time').toMillis' .如果我重新加载应用程序,导航回有问题的屏幕,消息出现,时间数据也出现。所以我的猜测是 firebase 调用的 promise 性质的延迟发生了一些事情,并且延迟导致空时间值的初始化,但它的时间足以使应用程序崩溃。

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

最佳答案

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

如果不小心,这可能会导致您的应用“闪烁”,因为它会将数据标记两次。

要更改待定时间戳的行为,您可以传递 SnapshotOptions对象作为 doc.data() 的最后一个参数和 doc.get()视情况而定。

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

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

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

如果您想忽略这些中间“本地”事件以支持等待服务器的完整响应,您可以使用:

.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,直到服务器响应任何新数据。

关于javascript - 为什么 Firestore 的 'doc.get(' time').toMillis' 会产生空类型错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60750300/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com