NGRXでの状態とアクションの強い入力



強い状態と行動をタイプする
NGRXストアでの作業時には、状態とアクションの両方に対して強く、明示的な型を提供することを強く推奨します.我々のアプリケーションが必然的に成長するので、これはさらに重要になります.そして、それはより多くの特徴とほとんど確かに途中で若干のリファクタリングを必要とすることを意味します.強力なタイプがこのプロセスをより簡単で安全にするかもしれないところです.
私は、我々は好きなことができるか嫌いになる写真のリストを表示できる簡単な角度のアプリにこの記事をベースにします.あなたは私のこのアプリケーションのソースコードを見つけることができますGitHub repo . この記事のコードに従ってくださいstrongTypingState_entryPoint タグ.git clone [email protected]:ktrz/introduction-to-ngrx.git
git checkout strongTypingState_entryPoint
クローニング後、すべての依存関係をインストールします.yarn install実行している例のアプリを見ることができますyarn start -o
アクションの入力
NGRXの最新バージョンでは、入力アクションは非常にまっすぐです.引用符docs :

The createAction function returns a function, that when called returns an object in the shape of the Action interface. The props method is used to define any additional metadata needed for the handling of the action. Action creators provide a consistent, type-safe way to construct an action that is being dispatched.


写真の好みや色を消すためのアクションの作成は次のようになります.
// src/app/store/photo.actions.ts

import {createAction, props} from '@ngrx/store';

export const likePhoto = createAction(
  '[Photo List] Like Photo',
  props<{id: string}>()
);

export const dislikePhoto = createAction(
  '[Photo List] Dislike Photo',
  props<{id: string}>()
);
これは簡潔かつ安全なアクションとアクションクリエーターオールインワンを作成します.また、以前のバージョンのNGRXで使用されていたクラスのアプローチとして、多くのboilerplateを生成しません(そして、多くのプロダクションコードリポジトリにまだ見つけられます).
// src/app/store/photo.actions.ts

import {Action} from '@ngrx/store';

const enum PhotoActionTypes {
  LikePhoto = '[Photo List] Like Photo',
  DislikePhoto = '[Photo List] Dislike Photo'
}

class LikePhoto implements Action {
  readonly type = PhotoActionTypes.LikePhoto;

  constructor(public readonly id: string) {}
}

class DislikePhoto implements Action {
  readonly type = PhotoActionTypes.DislikePhoto;

  constructor(public readonly id: string) {}
}

export type PhotoActions = LikePhoto | DislikePhoto;
この例では、アクションクリエーターとして機能する各アクションのクラスを作成する必要があります.しかし、すべての可能なアクションタイプをenumまたは1セットのconstsと別の型PhotoActions 後で使用するために.すべてのこの行動はきちんとcreateAction ユーティリティ機能は、新しいアクションを作成するために、私は非常にそれを使用することをお勧めします.

タイピング状態
状態を入力すると、それは個々の特定の機能を個別に含むすべてのスライスを入力するのに良い練習です.それを含む良い場所は状態のこの特定のスライスを扱う還元ファイルです.大きなプロジェクトの場合は、別のファイルIEで状態型を保つこともできます.src/app/store/photo.state.ts
// src/app/store/photo.state.ts

export interface Photo {
  id: string;
  title: string;
  url: string;
  likes: number;
  dislikes: number;
}

export interface PhotoState {

}

export const photoFeatureKey = 'photo';

export interface PhotoRootState {

}

NGRXチェーンの残りの部分を(暗黙のうちに)入力する
両方の状態とアクションを強く入力することによって、すべての作成された減速、セレクタ、およびエフェクトは、簡単にさらなるタイプを推測することができますし、残りのNGRXチェーンタイプを安全に保つことができます.
import {createReducer, on} from '@ngrx/store';
import {dislikePhoto, likePhoto} from './photo.actions';
import {PhotoState} from './photo.state';

const initialState: PhotoState = {};

export const photoReducer = createReducer(
  initialState,
  on(likePhoto, (state, action) => ({
    ...state,
    [action.id]: {
      ...state[action.id],
      likes: state[action.id].likes + 1
    }
  })),
  on(dislikePhoto, (state, action) => ({
    ...state,
    [action.id]: {
      ...state[action.id],
      dislikes: state[action.id].dislikes + 1
    }
  }))
);
提供することでinitialState to createReducer ユーティリティ機能photoReducer は、PhotoState 種類
それぞれon(...) コールは、与えられたアクションから型スクリプト型推論を使用しますlikePhoto , dislikePhoto ) それで
on(likePhoto, (state, action) => {/* ... /*})
は、
// this is a bit simplified type than the actual inferred type
// for a sake keeping it easier to grasp
type LikeActionType = {id: string, type: '[Photo List] Like Photo'}

on(likePhoto, (state: PhotoState, action: LikeActionType): PhotoState => {/* ... /*})
同じ規則は、我々の州からセレクタを構築することに当てはまります
// src/app/store/photo.selectors.ts

import {createFeatureSelector, createSelector} from '@ngrx/store';
import {photoFeatureKey, PhotoRootState, PhotoState} from './photo.reducer';

const selectPhotoFeature = createFeatureSelector<PhotoRootState, PhotoState>(photoFeatureKey);

export const selectPhotos = createSelector(selectPhotoFeature, state => Object.keys(state).map(key => state[key]));

export const selectPhoto = createSelector(selectPhotoFeature, (state: PhotoState, props: {id: string}) => state[props.id]);
強い明示的な表現を提供することでselectPhotoFeature , タイプスクリプトは、通常、それから派生した他のすべてのセレクタのタイプを推論することができます.新しい派生セレクタを作成するとき:
export const selectPhotos = createSelector(selectPhotoFeature, state => Object.keys(state).map(key => state[key]));
このようにすべてを明示的に入力するのと等価です
export const selectPhotos = createSelector<PhotoRootState, PhotoState, Photo[]>(selectPhotoFeature, (state: PhotoState): Photo[] => Object.keys(state).map(key => state[key]));
すべてのユースケースが自動的に推論することができますが、通常、TSコンパイラのための小さなヒントが十分です
export const selectPhoto = createSelector(selectPhotoFeature, (state, props: {id: string}) => state[props.id]);
state PARAMは自動的に推論できず、any デフォルトでタイプ.角度は厳しいモードでそれについて不満を言うので、入力を完了するために、我々は明らかに適切なを加えることができますPhotoState ここでタイプします.
export const selectPhoto = createSelector(selectPhotoFeature, (state: PhotoState, props: {id: string}) => state[props.id]);

利益
結論として、ただのアクションと状態のための強力なタイピングを提供することによって、私たちはNGRXチェーンの他の部分で通常自由に(例えば、TSコンパイラのために最小限のヒントを提供することによって)タイプを得ます.これは、コードの書き込み時にIDEの自動補完の両方に恩恵をもたらすことを意味します.また、いくつかのリファクタリングを行う場合や状態に新しい機能を追加する場合は、安全ネットを提供します.たとえば、新しい機能に対応するために状態の形を変更すると、すぐに、アプリケーションのチェーンの他の部分が影響を受けるTSコンパイラまたはIDEによって通知されます.このように我々はより簡単にそれらのすべてを確認することができます.高いテストカバレッジを使用すると、プロセス内の何も壊すことなくコードを変更することができます.
あなたはこの記事の最後の結果のコードを見つけることができますGitHub repo . チェックアウトstrongTypingState_ready タグを取得すると最新のソリューションを実行する準備ができました.
あなたが質問をするならば、あなたは常につぶやきまたはDM私を缶詰にします.私は常に助けて幸せです!
このドットラボは、企業のデジタル変換の努力を実現支援に焦点を当てた現代のWebコンサルティングです.専門家の建築指導、訓練、またはコンサルティング、角度、Vue、Webコンポーネント、Graphql、ノード、バゼル、またはポリマー、訪問thisdotlabs.com .
このドットメディアは、すべての包括的で教育的なウェブを作成することに集中します.我々は最新のイベント、ポッドキャスト、および無料のコンテンツを介して近代的なWebの進歩と最新の状態に保つ.学ぶthisdot.co .