CRUD 操作后如何更新异步管道数据/状态?

问题描述 投票:0回答:1

这是一个简单的例子。我有一个可观察的

users$
存储从 API 获取的用户。我还有另一个可观察的
usersWithAction$
,当 CRUD 操作发生时,它使用
scan
运算符封装状态。

我对我的

usersWithAction$
可观察值有疑问。尽管我使用
merge
运算符的方法工作正常,但我觉得这个解决方案很糟糕,必须有更好的方法来实现。如果有任何建议或相关信息的链接,我将不胜感激。谢谢。

堆栈闪电战

这里是data.service.ts

interface User {
  id: number;
  name: string;
  phone: string;
}

type ActionDelete = { action: 'delete'; id: number };
type ActionAdd = { action: 'add'; user: User };

@Injectable({ providedIn: 'root' })
export class DataService {
  constructor(private http: HttpClient) {}

  private actionSubject = new BehaviorSubject<'' | ActionAdd | ActionDelete>(
    ''
  );
  action$ = this.actionSubject.asObservable();

  users$ = this.http
    .get<User[]>('https://jsonplaceholder.typicode.com/users')
    .pipe(shareReplay(1));

  usersWithAction$ = merge(this.action$, this.users$).pipe(
    scan((acc, value) => {
      if (Array.isArray(value)) return [...value];
      else {
        if (value && value.action === 'delete') {
          return acc.filter((user) => user.id !== value.id);
        }
        if (value && value.action === 'add') {
          return [...acc, value.user];
        }
      }
    }, []),
    tap((data) => console.log(data, 'data')),
    shareReplay(1)
  );

  deleteUser(id: number) {
    // Do some API related requests
    this.actionSubject.next({ action: 'delete', id: id });
  }

  addUser() {
    // Do some API related requests
    this.actionSubject.next({
      action: 'add',
      user: { id: 123, name: 'John Doe', phone: '234234' },
    });
  }
}

这里是app.component.ts:

export class AppComponent {
  name = 'Angular ' + VERSION.major;
  constructor(private dataService: DataService) {}

  users$ = this.dataService.usersWithAction$;

  deleteUser(id: number) {
    this.dataService.deleteUser(id);
  }

  addUser() {
    this.dataService.addUser()
  }
}

这里是app.component.html

<div *ngFor="let user of users$ | async" class="user-container">
  <p>{{ user.name }}</p>
  <p>{{ user.phone }}</p>
  <button type="button" (click)="deleteUser(user.id)">Delete</button>
</div>
<button type="button" (click)="addUser()">Add</button>
angular rxjs
1个回答
0
投票

以下是我认为可以简化代码的一些更改:

  • 使用普通的
    Subject
    代替
    BehaviorSubject
    进行操作
    • 不需要初始值行为
  • 将行动主题保密,因为这是内部问题
  • 而不是
    merge
    ,使用获取的用户列表开始流,然后切换到发生修改的操作流的结果
    • IMO 这更清楚地传达了获取的列表和操作之间的关系,因为首先使用列表,然后应用操作
private action$ = new Subject<ActionAdd|ActionDelete>();

users$ = this.http.get<User[]>('https://jsonplaceholder.typicode.com/users').pipe(
  switchMap(initialUsersList => this.action$.pipe(
    scan((users, action) => {
      if (action.type === 'add') {
        return users.concat(action.user);
      }
      if (action.type === 'delete') {
        return users.filter(user => user.id !== action.id);
      }
    }, initialUsersList),
    startWith(initialUsersList),
    shareReplay(1),
  )),
);

注意使用

startWith()
来最初发出获取的列表。我们还使用
initialUsersList
而不是空数组来初始化扫描状态。

这是一个 StackBlitz 演示。


如果愿意,您可以将状态更新逻辑提取到自己的纯函数中以提高可读性:

users$ = this.http.get<User[]>('https://jsonplaceholder.typicode.com/users').pipe(
  switchMap(initialUsersList => this.action$.pipe(
    scan(usersListReducer, initialUsersList),
    startWith(initialUsersList),
    shareReplay(1)
  )),
);

这是一个有效的 StackBlitz 演示。

© www.soinside.com 2019 - 2024. All rights reserved.