【发布时间】:2019-08-16 16:42:42
【问题描述】:
我正在开发一个像 Facebook 这样的带有墙的应用程序,其中包含帖子组件(具有点赞计数器)和嵌套的 cmets 组件,这些组件通过帖子内的按钮按需加载。我正在使用 NgRx 商店,当我按照以下步骤操作时出现意外行为:
- 我使用为此目的提供的发布按钮中的按钮打开 cmets,它们正确加载
- 我点击赞按钮,然后引发
LikePost动作 - 然后触发
LikePostSuccess操作,将新的帖子对象(更新的点赞数)作为有效负载。然后更新点赞计数器。 - 但问题是:当
post.likesCount属性被更新时,子CommentsComponent被重新初始化(而内部没有数据发生变化)并且它使显示闪烁。
它是如何实现的以及到目前为止我尝试了什么:
- 我使用
@ngrx/entity和两个不同的reducer 来管理帖子和评论。 - 在
PostsComponent,我设置changeDetection: ChangeDetectionStrategy.OnPush - 我注意到当状态以不可变的方式完全更改时会发生这种行为(通过使用
adapter.UpdateOne()方法),但如果我只是以可变的方式直接更改属性post.likesCount,它可以解决问题,但是这是一种不好的做法。 - 我尝试在组件中显示此
likesCount,并使用从为此目的(likesCount$(post) | async as likesCount)制作的选择器中获取的 Observable。然后我以为数据的引用不会动,不会有子初始化,但它不起作用。
posts.component.html
<ng-container *ngFor="let post of posts">
<div>{{ post.title }}</div>
<button (click)="likePost(post)">
<mat-icon>thumb_up</mat-icon>
<span>{{ post.likesCount }}</span>
</button>
<button (click)="toggleComments(post)">
<mat-icon>chat_bubble</mat-icon>
</button>
<div>
<ng-container *ngIf="comments$(post) | async as comments">
<app-comments [comments]="comments" [postId]="post.id"></app-comments>
</ng-container>
</div>
</ng-container>
cmets.component.html
<ul>
<li *ngFor="let comment of comments">{{ comment.content }}</li>
</ul>
posts.reducer.ts
const reducer = createReducer(
initialState,
// ...
on(postsActions.likePostSuccess, (state, props) => {
const update: Update<Post> = {
id: props.post.id,
changes: {
likesCount: props.post.likesCount
}
};
return postsAdapter.updateOne(update, state);
})
// ...
);
posts.component.ts
// ...
comments$(post: Post): Observable<UserComment[]> {
return this.store.pipe(select(selectCommentsByPostId(post.id)));
}
// ...
最后,问题是:如何在不触发子初始化的情况下更新父组件属性?这里的问题是孤立的,但我无法想象如果我有许多嵌套组件会发生什么。
问题是否来自 ChangeDetection?从存储状态更新的方式(可变/不可变)?使用@ngrx/entity库是一个不错的选择吗?
感谢您的帮助
【问题讨论】:
-
你能发布那个 cmets$(post) 函数吗
-
你应该尝试
trackBy来避免创建元素 - netbasal.com/… -
@user2216584 成功了!非常感谢!请将此评论添加为答案,我会将其标记为“已接受”