Angular+Firebase出现TypeError:对象不可扩展,请求排查连锁调用问题
问题根源
你碰到的TypeError: Cannot add property 0, object is not extensible错误,本质是Firebase SDK尝试修改被NGRX冻结的对象。
NGRX默认开启的strictActionImmutability/strictStateImmutability运行时检查,会冻结所有通过action传递的对象。当你调用Firebase认证接口(比如signInWithEmailAndPassword)后,若将返回的User或UserCredential直接作为action payload传入NGRX,Firebase内部关联的上下文对象会被连带冻结。后续调用Firestore的onSnapshot时,SDK需要向内部观察者队列数组push元素,而这个数组已经被NGRX冻结,就会触发报错。
另外,单个Effect运行正常是因为没有触发认证后的对象冻结连锁反应,只有当认证action/Effect先执行,后续Firestore操作才会受影响。
排查与修复方案
1. 快速验证问题根源
临时关闭NGRX的action不可变检查,确认报错是否消失:
// 在StoreModule.forRoot配置中修改 StoreModule.forRoot(reducers, { runtimeChecks: { strictActionImmutability: false, // 临时关闭 strictStateImmutability: true, // 保留状态不可变检查 // 其他原有检查项 } })
如果报错消失,就坐实了是NGRX冻结action payload导致的问题。
2. 推荐修复:克隆认证对象再传入action
不要直接把Firebase返回的原生对象丢给NGRX action,而是深度克隆后只传递需要的字段,避免Firebase内部对象被冻结:
// 认证Effect中的处理示例 signIn$ = createEffect(() => this.actions$.pipe( ofType(AuthActions.signIn), switchMap(({ email, password }) => this.authService.signInWithEmailAndPassword(email, password).pipe( map(userCredential => { // 深度克隆用户对象,剥离Firebase原生属性 const safeUser = JSON.parse(JSON.stringify(userCredential.user)); return AuthActions.signInSuccess({ user: safeUser }); }) ) ) ) );
这样NGRX冻结的只是普通的JS对象,不会影响Firebase SDK内部的原生实例。
3. 备选修复:隔离Firestore监听上下文
把Firestore的监听逻辑从Effect中移到独立服务里,避免NGRX的冻结机制影响SDK内部操作:
@Injectable({ providedIn: 'root' }) export class FirestoreService { constructor(private firestore: Firestore) {} watchDocument<T>(docPath: string, docId: string): Observable<DocumentSnapshot<T>> { const docRef = doc(this.firestore, docPath, docId) as DocumentReference<T>; return new Observable(subscriber => { // 手动管理订阅,确保上下文不受外部冻结影响 const unsubscribe = onSnapshot(docRef, { next: snapshot => subscriber.next(snapshot), error: err => subscriber.error(err) }); // 返回取消订阅函数,让Observable自动管理生命周期 return unsubscribe; }); } }
验证流程
- 用方案1快速确认问题根源;
- 用方案2或3修复代码;
- 测试“认证→调用watchDocument”的完整流程,确认报错消失。
内容的提问来源于stack exchange,提问作者J King

