Angular After Tutorial を分からないところがなくなるまでやりたい



https://zenn.dev/lacolaco/books/angular-after-tutorial/viewer/introduction

状態管理の部分が分からなすぎて今まで手をつけてなかったのですが、意を決してやってみます。
- とりあえず調べて記録した部分から公開していきます。
- めっちゃ基本的な部分も分かってませんが悪しからず。


## Angular After Tutorialで学べること

- コンポーネントは単一責任原則にしたがって親子関係を分割する
- コンポーネントがもつべきでないビューと関係のない処理はサービスに移動する
- コマンド・クエリ分離原則にしたがって、副作用の発生し得る箇所を限定する
- ビューとの結合性が高く、単独の責務として定義しづらいビジネスロジックをUsecaseに集約し、API呼び出しや状態管理などのサービスの独立性を維持する

---



- `_state$`の`_`の意味


- `queueScheduler`
- `select<T>()`の<T>の意味
- `distinctUntilChanged`
- `firstValueFrom`

https://zenn.dev/bje35149/articles/344cf89d340f60

- usecaseとは

https://kasaharu.hatenablog.com/entry/20210414/1618399842

https://typescriptbook.jp/tips/merge-objects

---

ひとまず、何やってるのかほとんど理解できない`store.service.ts`の読解を行う。


### `store.service.ts`

```typescript:store.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, queueScheduler } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs/operators';
import { State, initialState } from '../shared/state.model';

@Injectable({
  providedIn: 'root'
})
export class StoreService {

  private _state$ = new BehaviorSubject<State>(initialState);

  constructor() { }

  update(fn: (state: State) => State) {
    const current = this._state$.value;
    queueScheduler.schedule(() => {
      this._state$.next(fn(current));
    });
  }

  select<T>(selector: (state: State) => T) {
    return this._state$.pipe(
      map(selector),
      distinctUntilChanged(),
    )
  }
}
```

#### プロパティ

##### `_state$`

  - Observable型の変数の末尾に`$`をつけるのは知ってた。
  - privateな変数の頭に`_`（アンダースコア）をつけることがあるそう。
  - あとは予約語を変数名に使いたいときに`_`をつけることがあるそう？
  - `_state$`でググるとgithubのコードとか結構出てくるので、状態管理をするときによく使うのだろうか？

https://teratail.com/questions/18728

##### `BehaviorSubject`
  - 「Observableに対し値を流すためのデータソース」の役割を担うクラス。
  - `Subject`が値を保持しないのに対し、`BehaviorSubject`は最後に更新された値を保持する。
  - インスタンス生成時に値の初期値を設定（今回で言えば`initialState`）

https://qiita.com/seapolis/items/97258d7c2b11148d0a81

---

#### `update`

```typescript:store.service.ts
update(fn: (state: State) => State) {
  const current = this._state$.value;
  queueScheduler.schedule(() => {
    this._state$.next(fn(current));
  });
}
```

##### `update(fn: (state: State) => State){ }`

- 関数の引数に関数を渡せる。

##### `(state: State) => State`
- 関数内のコードが式1つだけの場合は、中カッコ`{}`と`return`が省略できる。
- `fn`は、State型の引数`state`を受け取って、関数内でなんやかんやし、State型の何かを返す関数。

https://typescriptbook.jp/reference/functions/arrow-functions

##### `this._state$.next(fn(current));`
- 現在の`_state$`になんやかんや手を加えて、また`_state$`に流している。
- この「なんやかんや」は、`fn`がどんな関数なのかによる。
- よく分からんけど、`fn`によって`current state`を好きなように変えられるのがミソで、再利用がしやすそう。

- `queueScheduler`
  - よく分からん・・・

http://www.fumiononaka.com/Business/html5/FN1806005.html

呼び出し側

```typescript:user-list.usecase.ts
async fetchUsers() {
  const users = await this.userApi.getAllUsers();

  this.store.update(state => ({
    ...state,
    userList: {
      ...state.userList,
      items: users
    }
  }));
}

setNameFilter(nameFilter: string) {
  this.store.update(state => ({
    ...state,
    userList: {
      ...state.userList,
      filter: {
        nameFilter
      }
    }
  }));
}
```
`update`の引数で渡している関数について、
`fetchUsers`なら`state`内の`items`のみを、`setNameFilter`なら`filter`のみを更新できるようになっている。

https://typescriptbook.jp/tips/merge-objects

##### `state => ({　})`

- 戻り値がオブジェクトリテラルの場合は、オブジェクトリテラルをカッコ`()`で囲む必要がある。

https://typescriptbook.jp/reference/functions/arrow-functions

---

#### `select`

`_state$`から必要な情報を取り出すメソッド

```typescript:store.service.ts
select<T>(selector: (state: State) => T) {
  return this._state$.pipe(
    map(selector),
    distinctUntilChanged(),
  )
}
```

##### `<T>`

- ジェネリクスは、コードの共通化と型の安全性を両立するための言語機能。
- `<T>`は型変数名の定義で、慣習としてTがよく使われるがAでもTypeでも構わない。

https://typescriptbook.jp/reference/generics

https://qiita.com/k-penguin-sato/items/9baa959e8919157afcd4#generics%E3%81%A8%E3%81%AF

##### `distinctUntilChanged`
- 直近最後に流れてきた値と異なる場合のみ値を流す

https://rxjs.dev/api/operators/distinctUntilChanged

https://qiita.com/maechabin/items/0436f777a0c80d672331


呼び出し側

```typescript:user-list.usecase.ts
get users$() {
  return this.store
    .select(state => state.userList)
    .pipe(
      map(({ items, filter }) =>
        items.filter(user => 
          (user.first_name + user.last_name).includes(filter.nameFilter)
        )
      )
    );
}

get filter$() {
  return this.store.select(state => state.userList.filter);
}
```

ジェネリクスを使わない場合、`users$`用の`selectUsers`と`filter$`用の`selectFilter`を用意しないといけないところ、ジェネリクスのおかげで`select`ひとつにまとめられた（？）