Firebase中updateDoc()失败时如何阻止Redux dispatch执行
实现方案
核心逻辑很简单:所有依赖Firebase更新成功的操作(包括Redux dispatch、成功提示等),必须放在await updateDoc()执行完成之后,绝对不能写到异步逻辑的外面。
之前代码的问题
- 第一版把成功提示、dispatch写在异步函数调用的外层,JS的异步逻辑是非阻塞的,调用
updateData()之后不会等它执行完,会立刻跑后面的同步代码,不管更新成没成都会触发本地状态修改,必然出现数据不一致。 - 第二版虽然把dispatch挪到了await后面,但有几个明显bug:一是写了两个
await重复调用updateDoc,会发两次重复的更新请求;二是靠window.navigator.onLine判断网络状态完全不可靠,这个API只能识别设备有没有连路由器/网卡,识别不了有没有实际公网连接,比如连了断网的wifi、请求中途断网的场景根本判断不出来;三是没有做重复点击防护,用户快速点多次会发多个并发请求,可能出现旧请求覆盖新状态的问题。
最终可靠实现
直接用try/catch包裹整个异步流程就行,不需要自己额外做网络前置判断——Firebase SDK本身会把所有失败场景(没网、权限不足、参数非法、服务端报错)都以抛出错误的形式返回,只要await updateDoc()没抛错,就代表文档已经在Firebase侧更新成功,这时候再触发dispatch就不会有问题。
首先在组件里加个ref做请求锁,防止重复触发:
const isUpdatingRef = useRef(false);
然后是事件处理逻辑:
const handleColor = async (e: React.FormEvent<HTMLInputElement>): Promise<void> => { // 重复点击直接拦截,避免并发请求 if (isUpdatingRef.current) return; isUpdatingRef.current = true; const targetColorValue = colors[e.currentTarget.value]; const docRef = doc(db, 'collection', currentUser.uid); try { // 等待Firebase确认更新成功,这一步抛错会直接跳去catch块,不会执行后面的dispatch await updateDoc(docRef, { customColor: targetColorValue }); // 到这一步100%更新成功,再更新本地Redux状态 dispatch(addColor({ select: 'customColor', value: targetColorValue })); } catch (err: any) { let tip = '更新失败,请稍后重试'; // 按错误码映射用户提示 switch (err?.code) { case 'network-request-failed': case 'unavailable': tip = '网络连接异常,请检查网络后重试'; break; case 'permission-denied': tip = '无权限修改该配置'; break; // 其他错误码按需加映射即可 } alert(tip); } finally { // 不管成功失败都释放锁 isUpdatingRef.current = false; } };
几个关键说明
- 不需要自己提前判断网络状态:Firebase SDK抛出的
network-request-failed、unavailable错误已经覆盖了所有网络异常场景,包括连不上网、请求超时、服务端不可用,比自己写navigator.onLine判断靠谱得多。 updateDoc本身返回Promise<void>,只要这个Promise正常resolve(不抛错),就代表服务端已经持久化写入成功,不需要额外判断返回值,逻辑和调用MongoDB接口判断响应成功是一样的。- 不要在try块外面、异步函数外面写任何依赖更新成功的逻辑,否则一定会出现更新失败但本地状态先改了的不一致问题。
内容的提问来源于stack exchange,提问作者Richardson
相关产品推荐
相关产品推荐

