@ngrx 在递归使用智能组件时从存储中选择特定的状态片

Posted

技术标签:

【中文标题】@ngrx 在递归使用智能组件时从存储中选择特定的状态片【英文标题】:@ngrx select specific slice of state from store when using a smart component recursively 【发布时间】:2018-06-16 00:22:54 【问题描述】:

我是ngrx 的新手,遇到了这个小问题,我还没有弄清楚如何解决。

基本上我有一个ListComponent,它从ngrx 存储中呈现ListItemComponents 的数组。

@Component( 
  ...
  template: `
    <list-item *ngFor="let item of item$ | async" [data]="item">
    </list-item>
  `
)
export class ListComponent implements OnInit 
  item$: Observable<ListItem>;
  constructor(private store: Store<ListState>) 

  ngOnInit() 
    this.item$ = this.store.select(selectListItems);
  

现在,在ListItemComponent 内部,在某些特定情况下,我想渲染另一个ListComponent,我可以在其中添加项目,但这不起作用,因为我收到Maximum call stack size exceeded 错误。

我的猜测是,如果我错了,请纠正我,因为嵌套的 ListComponent 正在访问与根 ListComponent 相同的状态片段,它只是试图一遍又一遍地嵌套和呈现相同的列表再次进入无限。

那么问题来了,我应该如何组合我的选择器以便在状态树中为每个嵌套的ListComponent 提供正确的入口点?


编辑

这是一个working stackblitz project,现在我的应用程序中在ListItem 内呈现List 的逻辑不同,但问题是相同的。

【问题讨论】:

你只需要在ListComponent中将selectListItems作为Input传递 想详细说明这将如何解决我的问题?我的意思是,为了将函数作为输入传递,我应该让我的ListStepComponent 商店也知道吗? 你能把selectListItemsListStepComponent的代码贴出来吗? 是的,给我一些时间来重现 stackblitz 中的示例 1 使用应用程序组件控制数据 - 智能组件,2 将项目分配为 @Input,3. 将状态更改为树结构,如 items:[name:'item1',items:[] ,name:'item2',items:[]] 【参考方案1】:

我认为你是对的:

我的猜测是,如果我错了,请纠正我,因为嵌套的 ListComponent 正在访问与根 ListComponent 相同的状态片,它只是试图一遍又一遍地嵌套和渲染相同的列表到无穷大.

尝试更新ListComponent 以接受ListItemComponent 的组件输入变量。请参阅工作示例https://stackblitz.com/edit/angular-ngrx-recursive-components-7mzncj

在该示例中,第 5 个ListItemComponent 也呈现ListComponent。但是,当单击第 5 个 ListItemComponent 上的“将元素添加到列表”按钮时,代码仍在将新元素添加到父列表中。我猜你会想要更新你的处理逻辑。不确定您要完成什么。

核心问题是列表是自己生成的。因此,如果一个列表生成另一个列表,则会遇到递归问题。通过使用组件输入变量,您可以阻止列表自行生成并消除递归问题。

更新:

我自己来了这个答案,但我只是注意到@FanCheung,在 cmets 中,之前提出了基本上这个解决方案。

【讨论】:

是的,在阅读@FanCheung 评论后,我基本上得出了与您提供的相同的解决方案。现在的问题是在相应的子列表中添加新生成的列表项。我想我应该更新reducer以遍历整个树,从调度动作的地方找到正确的节点(也许有一些唯一的ID?)并返回状态......我看到这种方法的问题是,因为选择器正在选择根列表,每当我添加嵌套步骤时,它都会更新整个状态树,而我希望选择器只影响相应的分支 @OsmanCea 实际上我认为您不必担心更新整个状态树(至少在性能方面)。如果你推出一个新的状态树,但它只有一点点改变,我认为 Angular 足够聪明,可以更新那些实际改变的 DOM 片段。是的,从角度来看差异会有一些开销,但我认为它可以忽略不计(除非你的列表真的很大,我想)。 例如,如果您在商店中订阅了一个列表,然后在列表中附加了一些内容,您的订阅将收到一份全新的列表副本。 Angular 足够聪明,only 实际上在最后添加了新项目。如果您的列表项都由它们自己的组件呈现,您可以在ngOnChanges 期间通过console.log 进行测试。当您将某些内容添加到列表时,您会注意到现有列表元素不会运行它们的OnChanges。唯一的区别是添加一个新元素。 Angular 非常聪明 :) 因为没有人愿意给我一个答案,所以你得到了赏金;) 我确实解决了我的问题(这意味着为每个列表组件生成一些唯一的 id,获取给定 id 的路径,并做一些深度状态更新不可变的东西,但是嘿,这是值得的)

以上是关于@ngrx 在递归使用智能组件时从存储中选择特定的状态片的主要内容,如果未能解决你的问题,请参考以下文章

ngrx 商店选择仅订阅特定操作

带有分页功能的 NgRx 存储

Ngrx - 选择器在存储更改后没有发出新值

在 angular/ngrx 应用程序中将状态保存到本地存储的逻辑的位置

Angular 7、Ngrx、Rxjs 6 - 访问延迟加载模块之间的状态

NgRx 无法从商店中选择