关于从 NgRx store 中选择数据的问题

Questions about selecting data from NgRx store

@Component({
  selector: 'app-user',
  styleUrls: ['./user.component.css'],
  template: `
    <button (click)="clickSearch('some keyword')">Search</button>

    <div *ngIf="books$ | async as books">

      <h5 *ngIf="hasSearched">No. of books: {{ books.length }}</h5>

      <p *ngIf="hasSearched && !books.length">No books found</p>

      <p *ngIf="!hasSearched">Please enter a search keyword to start searching for books.</p>

      <h2 *ngFor="let book of books">{{ book.title }}</h2>

    </div>
  `
})
export class UserComponent {

  constructor(private store: Store) {}

  hasSearched: boolean = false;

  books$ = this.store.select(BooksSelector.getBooks)
             .pipe(
               tap(data => console.log(data))
             );

  clickSearch(keyword: string) {

    this.hasSearched = true;

    this.store.dispatch(BooksActions.loadBooksFromApi(
      { q: keyword }
    ));

  }
}

假设我有一个类似于上面的简化 Angular 组件代码。流量:

  1. 当我们第一次进入这个页面时,store.select() 语句立即 return 存储 [] 的初始状态值。这将导致模板显示“未找到书籍”和“书籍数量 = 0”消息,这是我不想要的。所以为了防止它,我添加了 hasSearched 布尔值 属性 并改为显示“请输入...”消息。

  2. 单击“搜索”按钮时,hasSearched 属性 变为 true,而 API 检索数据时,模板将简要显示“未找到书籍”和“书籍数量=0”消息。我不希望它以这种方式运行。我希望消息等待(通过隐藏)直到 API 调用完成。

  3. 当API调用完成后,图书列表正常出现在模板中,消息也正确显示。

  4. 在用户搜索某些内容并获得一些结果后,然后离开页面并 returns 到它,store.select() 语句将 return以前的商店价值。我不想要这个。我只是想让这个页面重新开始,没有书籍列表,只有消息“请输入搜索关键字开始搜索书籍。”。

我的问题针对第 2 点和第 4 点:

对于第 2 点,我是否应该添加另一个布尔值 属性 来检查加载状态?似乎有太多 *ngIf 标志。有没有更优雅的方式通过 NgRx 选择器来处理这个问题?

对于第 4 点,我该如何实现?是通过在 ngOnDestroy() 期间向商店推送一个空的 [] 来强制清除 Books 状态吗?

点数2

我建议在你的减速器中添加另一个布尔值 hasSearched: boolean,并从组件中删除 hasSearched。 使用组件中的选择器订阅 hasSearched。 当用户第一次进入该组件时,它应该是 undefined。由此,您可以显示您的消息

   <p *ngIf="(hasSearched | async) === undefined">
       Please enter a search keyword to start searching for books.
   </p>

当用户点击按钮时,您将调度您的操作 BooksActions.loadBooksFromApi。在相同的 action/reducer 中,当 API 调用正在进行时,将字段 hasSearched 转换为 false, 此 false 标志将阻止显示这两条消息:

<h5 *ngIf="(hasSearched | async)">No. of books: {{ books.length }}</h5>

<p *ngIf="(hasSearched | async) && !books.length">No books found</p>

完成后true。作为参考,您的 state 和 reducer 将如下所示。

    export interface BooksState {
       books: any;
       hasSearched: boolean;
    }

    export const initialState: BooksState = {
       books: undefined,
       hasSearched: undefined
    };

reducer will cases 看起来像这样。

    case BooksActionsTypes.loadBooksFromApi:
      return {
        ...state,
        hasSearched: false
     };

    case BooksActionsTypes.loadBooksFromApiSuccessful:
      return {
        ...state,
        books: action.payload.books
        hasSearched: true
     };

点数4

我的理解是正确的,通过在 ngOnDestroy() 上调度操作来清除状态。这与我使用的方式相同,当我想在下次访问同一组件时获得新状态时清除状态。这将类似于我在下面写的内容

    case BooksActionsTypes.ClearState:
      return {
        ...state,
        books: undefined
        hasSearched: undefined
       };