Angular与Componentstore实践示例

2023-02-18 08:41:23
目录
正文将逻辑部分分离到 service component store 的使用方法在 component 中使用在 component 中对于 store 的使用

正文

我们知道,Angular>service 和 rxjs 的存在,使得状态管理在 Angular,并非必须。

一个 BehaviorSubject 就形成了最简单的状态管理:

将逻辑部分分离到>

使用 Rxjs 将 service 中的逻辑部分,拆分为状态和方法。Component subscribe state 来更新 view, 调用方法来修改 state。

我们知道,Angular 中可以使用 NgRx 提供的 store 来做状态管理。NgRx store 跟 Redux 本质上是一样的。多数时候,可能我们都觉得 Redux 太重了。而且强行将数据与 view 分离。

除了 ngrx store, ngrx 还提供了另外一种轻量级的状态管理,component store。本质上它跟我们刚刚介绍的状态管理模式是一致的,只是提供了一些接口方便我们使用。 NgRx - @ngrx/component-store

在定位上,可以参考 hooks 与 redux 的区分,Ngrx store 用来处理全局状态,Component Store 用来 component 内局部的状态管理使用。(Store 和 Component 拥有相同的生命周期)。当然,实际上使用下来,component store 也完全可以用来当作全局的状态管理来处理。

 component>

我们可以看到,store 主要提供了三个方法:

    select, 用来拆分 stateupdater, 用来更新 state, 主要是无 effect 的 state 更新。effect, 用来处理有 effect 的情况,调用 updater 的方法来更新数据。

    我们可以看出来,这样的接口设计跟 Mobx 或者 Vuex 是比较接近的。区别就是,因为 RxJS 的缘故,它的实现异常简单。几乎只是基于 behaviorSubject 包了一层壳。

    有两点还是值得一提的:

    updater 和 effect 的方法参数可以同时接受 value 和 observable value, 这使得我们在操作一个 stream 的时候,可以直接将 stream 作为参数。

    比如我们现在有一个方法 updateUserName: (string | Observable<strring>) => void;

    使用的时候,我们可以直接调用:updateUserName('zhangsan')

    有时候我们在 component 里拿到的是一个 stream。

    比如 form.valueChanges, 这时候我们就不需要手动 subscribe stream, 而是直接

    updateUserName(form.valueChanges.pipe(map(form => form.userName)))

      updater 和 effect 将 stream 作为参数后会自动 subscribe 并在 store 被销毁的时候 unsubscribe, 这就意味着你不用再写一堆手动 unsubscribe 的逻辑。

      在>

      在 component 中使用也比较简单:

        将 component 中必要的数据投喂给 store, 一般来说是 input.在 component 中调用 updater 或者 effect 返回的方法修改 state。在 component 中 subscribe state 实现 view 层的渲染。
        @Component({
          template: `...`,
          // ❗️MoviesStore is provided higher up the component tree
        })
        export class MovieComponent {
          movie$: Observable<Movie>;
          @Input()
          set movieId(value: string) {
            // calls effect with value. ???? Notice it's a single string value.
            this.moviesStore.getMovie(value);
            this.movie$ = this.moviesStore.selectMovie(value);
          }
          constructor(private readonly moviesStore: MoviesStore) {}
        }
        

        当然,我们也可以做一点优化,比如,尽可能将逻辑放在 store 中 componet 只做简单的调用。将数据之间的联动关系放在 store 的 constructor 中,component 只做调用。

        @Component({
          template: `...`,
          // ❗️MoviesStore is provided higher up the component tree
        })
        export class MovieComponent {
          movie$: Observable<Movie>;
          @Input()
          set movieId(value: string) {
            this.mobiesStore.patchState({movieId: value});
          }
          constructor(private readonly moviesStore: MoviesStore) {}
        }
        
        @Injectable()
        export class MoviesStore extends ComponentStore<MoviesState> {
          constructor(private readonly moviesService: MoviesService) {
            super({movieId: string; movies: []});
            this.geMovie(this.movieId$);
          }
          movieId$ = this.select(state => state.movieId);
          movie$ = this.moviesStore.selectMovie(this.movieId$);
          readonly getMovie = this.effect((movieId$: Observable<string>) => {
            //....
          });
          readonly addMovie = this.updater((state, movie: Movie) => ({
            // ...
          }));
          selectMovie(movieId: string) {
             // ...
          }
        }
        

        因为这里的 component store 是针对单个 component 的,也就是通常情况它的使用场景是逻辑较为复杂的 component。一个 component 基于 input 的变化完全可以转化为对于 store 的监听。那么,我们基本上可以将 component 的多数 input 同步到 store 中。

        在一段时间的使用过程中,我发现,这是比较费劲的,

          同一个字段,我需要在 component 和 store state 中声明两次。Input 必须转写成 set 模式
          比如 userName 这个字段
          原来:
          @Input userName: string;
          与 store 同步:
          @Input
          set userName(val: string) {
            this.store.patchState({userName: val});
          }
          如果想在 component 中直接调用 userName 就更麻烦了。
          private _userName: string;
          @Input
          set userName(val: string) {
            this._userName = val;
            this.store.patchState({userName: val});
          }
          get userName() {
            return this._userName;
          }
          

          如果字段比较多,简直就是灾难。

          最近在尝试一种不同于官网推荐的方法。我们知道,除了 set 我们还有一种更为常规的方法获取 input changes, 那就是 ngChanges。

          export function mapPropChangesToStore<T extends JsonRecord>(this: ComponentStore<T>, mappedKeys: readonly string[], changes: SimpleChanges) {
            const state = mappedKeys.reduce((prev: Partial<T>, propKey) => {
              // eslint-disable-next-line @typescript-eslint/ban-ts-comment
              // @ts-ignore
              const propValue = changes?.[propKey];
              if (!propValue) {
                return prev;
              }
              return ({
                ...prev,
                [propKey]: propValue.currentValue,
              });
            }, {});
            if (isEmpty(state)) {
              return;
            }
            this.patchState(state);
          }
          

          在>
          import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
          import { mapPropChangesToStore } from '@dashes/ngx-shared';
          import { componentInputs, CsDemoStore } from './cs-demo.store';
          @Component({
            selector: 'app-cs-demo',
            templateUrl: './cs-demo.component.html',
            styleUrls: ['./cs-demo.component.css']
          })
          export class CsDemoComponent implements OnChanges {
            @Input() p1!: string;
            @Input() p2!: string;
            @Input() p3!: string;
            constructor(public store: CsDemoStore) { }
            ngOnChanges(changes: SimpleChanges): void {
              mapPropChangesToStore.bind(this.store)(componentInputs, changes);
            }
          }
          export const componentInputs = ['p1', 'p2'] as const;
          export type State = Pick&lt;CsDemoComponent, typeof componentInputs[number]&gt;;
          

          这样看起来就会简洁很多。

          以上就是Angular 与 Component store实践示例的详细内容,更多关于Angular Component store的资料请关注易采站长站其它相关文章!