Angular Ngrx Effect内重定向引发异常问题求助
问题分析与解决思路
这个异常的核心原因是尝试修改不可扩展(non-extensible)的对象,结合你的场景,最可能的触发点是路由守卫中直接引用了Firebase返回的不可扩展User实例,或者导航与守卫的时序冲突导致Angular内部尝试修改该对象。以下是具体的解决方向:
1. 复制Firebase User对象,避免直接引用不可扩展实例
Firebase的User对象是被冻结/密封的(不可扩展),当你的路由守卫返回包含原User实例的对象时,Angular Fire的守卫管道可能会尝试对该对象添加内部属性,从而触发Cannot add property 0, object is not extensible错误。
修改守卫代码,将原User实例浅拷贝为可扩展的普通对象:
export const userSpaceGuard = () => pipe( switchMap(async (user: User | null) => { if (!user) { return null; } // 浅拷贝User对象的属性,生成可扩展的新对象 const userCopy = { ...user }; return { user: userCopy, idTokenResult: await user.getIdTokenResult(), }; }), // ... 后续逻辑保持不变 );
2. 改用UrlTree作为守卫的重定向返回值
直接返回路由路径数组可能导致Angular内部对返回值进行额外处理,改用UrlTree可以避免这种潜在的对象修改操作,同时让守卫逻辑更清晰:
import { inject, Router } from '@angular/router'; export const userSpaceGuard = () => { const router = inject(Router); return pipe( switchMap(async (user: User | null) => { // ... 原有逻辑 }), map((userInfos: any) => { if (!userInfos?.user) { // 返回UrlTree而非路径数组 return router.createUrlTree(['auth/login']); } const claims = userInfos.idTokenResult.claims; if (claims && claims.role === UserRole.Admin) { return router.createUrlTree(['admin']); } return true; }) ); };
3. 确保导航在用户状态完全更新后执行
即使在userLoaded的Effect中执行导航,也需要确保状态已经完成更新。可以通过withLatestFrom从Store中获取用户状态,确认存在后再触发导航:
// 假设你有一个选择器用于获取当前用户 import { selectCurrentUser } from './auth.selectors'; loginSuccess$ = createEffect(() => this.actions$.pipe( ofType(AuthLoginActions.loginSuccess), switchMap(({ credentials }) => this.authService.me().pipe( map((user: MeQuery['me']) => AuthActions.userLoaded({ user })), catchError((error) => of(AuthActions.error({ error }))) ) ), // 等待Store中的用户状态更新完成 withLatestFrom(this.store.select(selectCurrentUser)), filter(([_, user]) => !!user), tap(() => this.router.navigate(['/'])) ) );
4. 确保Apollo返回的用户对象可扩展
如果Apollo开启了缓存对象冻结(比如cacheOptions.freeze: true),返回的用户对象会是不可扩展的。可以在me()方法中复制对象:
me(): Observable<MeQuery['me']> { return this.meGql.watch().valueChanges.pipe( map((result) => { // 复制对象,解除冻结 return { ...result.data.me }; }) ); }
内容的提问来源于stack exchange,提问作者Jérémie Dutheil
相关产品推荐
相关产品推荐

