RxJS中更新Session对象Players数组失败,求正确实现方案
问题描述
我开发了一个简易应用,支持用户创建会话、加入会话并为该会话创建用户名。当前在新用户加入会话时,更新「players(用户)」数据遇到问题。
我的思路是使用一个session可观察对象来检测变化,并将变化传递给子组件以更新UI:
相关代码
app.component.html
<app-join-session></app-join-session> <ng-container *ngIf="session$ | async as session"> <app-current-players [session]="session"></app-current-players> <app-new-user [session]="session"></app-new-user> </ng-container>
session.service.ts
export class SessionService { private subject = new ReplaySubject<Session>(); public session$ = this.subject.asObservable(); public setSession(session: Session) { this.subject.next(session) } public addUserToSession(session: Session): void { this.subject.next(session) } }
app.component.ts
protected session$ = this.sessionService.session$;
new-user.component.ts
public createUserName(userName: FormControl): void { this.apollo.mutate<CreateUser>({ mutation: userOperations.CREATE_USER, variables: { name: userName.value, sessionId: this.session.id, } }) .subscribe({ next: ({data}) => { this.session.players.push(data!.createUser) this.sessionService.addUserToSession(this.session) }, error: (e: GraphQLError) => { this.errorMessage = {...e}.message; } }) }
我原本计划将创建的用户推入session的players数组,再通过subject.next传递以更新可观察对象,但此方法无效,还出现错误:
Cannot add property 19, object is not extensible at Array.push ()
请问有没有更优的方法来更新包含新玩家的session数据流?
解决方案
1. 解决对象不可扩展问题
出现object is not extensible错误,是因为从异步流或Apollo返回的session对象被冻结(不可扩展),无法直接修改原对象。需要创建会话对象的新副本,再添加新用户。
2. 修正组件内的更新逻辑
修改new-user.component.ts中的代码,通过创建新对象来更新会话数据:
public createUserName(userName: FormControl): void { this.apollo.mutate<CreateUser>({ mutation: userOperations.CREATE_USER, variables: { name: userName.value, sessionId: this.session.id, } }) .subscribe({ next: ({data}) => { // 创建新的session副本,合并原数据与新玩家数组 const updatedSession = { ...this.session, players: [...this.session.players, data!.createUser] }; this.sessionService.addUserToSession(updatedSession); }, error: (e: GraphQLError) => { this.errorMessage = {...e}.message; } }) }
3. 封装更新逻辑到Service(可选优化)
把创建新会话对象的逻辑移到Service中,让组件代码更简洁:
// session.service.ts public addUserToSession(existingSession: Session, newUser: User): void { const updatedSession = { ...existingSession, players: [...existingSession.players, newUser] }; this.subject.next(updatedSession); }
组件中调用时只需传入原会话和新用户:
// new-user.component.ts this.sessionService.addUserToSession(this.session, data!.createUser);
4. 原方法无效的核心原因
- 原会话对象被冻结,无法直接修改其
players数组 - 即使对象可扩展,直接修改原对象后传递给
next,由于对象引用未变,Angular变更检测可能不会触发UI更新;创建新对象能确保可观察流发出全新值,触发所有订阅者更新UI。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

