Skip to content

Instantly share code, notes, and snippets.

@aliyome
Last active July 22, 2018 07:28
Show Gist options
  • Save aliyome/d99964563cc6009d3bb3c77da69f420f to your computer and use it in GitHub Desktop.
Save aliyome/d99964563cc6009d3bb3c77da69f420f to your computer and use it in GitHub Desktop.
雑多

Redux(ngrxの流れ)

sequenceDiagram

participant Component
participant Action
participant Reducer
participant Store
participant Effect
participant Service

Component ->> Action: dispatch(new LoginAction())

Action ->> Reducer: deal with LoginAction

Reducer ->>+ Store: update state
Store ->> Component: select() // notify state changed
Store ->>- Effect: notify effected action

Effect ->> Service: Http request (login)
Service ->> Effect: Http response
Effect -->> Action: dispatch(new LoginSuccessAction()) or failure
Action ->> Reducer: deal with LoginSuccessAction

Reducer ->>+ Store: update state
Store ->> Component: select() // notify state changed
Store ->>- Effect: nothing
Loading

開発フロー(コーディング)

graph TD

Component --> a



Loading

テスト観点

Component

  • Storeにdispatchした?
  • Storeからselectした?
spyOn(store, 'dispatch').and.callThrough();
spyOn(store, 'select').and.callThrough();
comp.ngOnInit();
expect(store.select).toHaveBeenCalled();
expect(store.dispatch).toHaveBeenCalled();

Effect

  • Actionが別Actionに変換された?
  • Effect内であるメソッドが呼び出されること(Router.navigate()など)
  • Effectのメタデータ
const action = new MyActions.HogeAction();
const completion = new MyActions.HogeActionSuccess();
actions = hot('--a-', {a: action});
const expected = cold('--b', {b: completion});
expect(effects.someSource$).toBeObservable(expected);


expect(metadata.someSource$).toEqual({dispatch: true});

Reducer

  • stateとactionを渡すと新しいstateが得られる
  • stateがimmutableであること
const state = setup();
const payload = setupPayload();
const expected = setupExpected();
deepFreeze(state);
const actual = hogeReducer(state, new HogeAction(payload));
expect(actual).toEqual(expected);

Action

  • typeが期待通りであること
  • payloadが期待通りであること
expect(action.type).toEqual(LOGIN);
expect(action.payload).toEqual({id: 999});

Selector

  • Selectorで取得した値が期待通りか
const actual = selector.getHoge(state);
expect(actual).toEqual('hoge');

Observableのテスト

// - a - b - c
// -: 0ms, a: 10ms, -: 20ms, b: 30ms
// coldは<number>、つまり1, 2, 3が流れる
const source$: Observable<number> = cold<number>('-a-b-c', {a:1, b:2, c:3});
const expected = '---B-C';
const values = {A:10, B: 20, C: 30};
const actual$ = mapFilterTest(source$);
ts.expectObservable(actual$).toBe(expected, values);
ts.flush();

function mapFilterTest(observable: Observable<number>): Observable<number>) {
    return observable
    .map(x => x * 10)
    .filter(x => x > 10);
}

やること

  • Moduleの使い方(Core, SharedはExportしていいのでは?)
  • Object.assignの代わりにspread operatorを使う {...hoge}
  • コンポーネント設計について、reduxの良いところが出せていない?
  • 同様にテストも微妙?
  • NativeElementのテストをやるのであれば、e2eテストで良いのでは?
  • NativeElementレベルのテストを毎回やりたいのであれば、reduxに拘らなくても良い気がする
  • reduxの良いところは、コンポーネントはStoreのみに依存できること
  • Angular v6がLTSなので、実際の開発が始まったらv6を入れたい。
    • v4もLTSなので、なんならv4に落としても良い。安心が欲しい。
  • ルーティングでchildrenを多用している理由は?
    • router-outletではなく、viewChildの方がUX的にもいいのでは?
      • 要検証
  • prettierでコードレビューを楽にしませんか?
    • フォーマットの強制
    • 徹底的に読みやすいフォーマットではなく、フォーマットを意識しないで良いという点が重要。
    • tslint --fixでも良いけど、足りないのでは
  • ActionTypesはenumで一覧管理できるようにする
  • actionのtypeはreadonlyで定数化
  • @ngrx/schematicsで雛形を作りたい
  • ngrx todo と、ngrx/exampleがクソほど勉強になる

開発の手順

前提として、画面設計後

  • HTMLで雑にUIを書く
  • 要素を切り出し → コンポーネントの検討をする
  • コンポーネントのテストを実装する
  • コンポーネントを実装する

forRoot, forChild

// Providersに自分自身を入れて、Rootに読み込ませるか
// Providersに自分が読み込まれた前提の物だけを入れて、自分に読み込ませるか
@NgModule({})
class A {
  static forRoot() {
    return {ngModule: A, providers: [AService]};
  }
  static forChild() {
    return {ngModule: A, providers: [BService]};
  }
}

@NgModule({
  imports: [A.forRoot()]
})
export class NonLazyLoadedModule {}

@NgModule({
  imports: [A.forChild()]
})
export class LazyLoadedModule {}

flexLayout便利そう

  • サンプルも秀逸、CSSで組む前に勉強できそう

MetaReducer

  • Reducerを作るReducer?
  • 要調査

覚書

@ngrx/schematicsの生成サンプルが便利 https://qiita.com/musou1500/items/8003c4a3f2b2e80d919f

concat,merge,switch,exhaustMapのわかりやすい説明 https://github.com/koolii/rxjs-beginner/blob/master/docs/switch-stream.md

laco [11:52 AM] CLIの1.7.0-beta.1で面白い機能が追加されました。 その名も budget 機能で、プロダクションビルドの生成物の許容サイズを定義して、一定値を超えると警告を出したり、ビルドを失敗扱いにしたりできる機能。

adwd [Jan 23rd at 6:28 PM] in #random ng.probe 知らなかった 😨 ng.probe($0)DebugElement が得られるんすねこれすげー!

1 reply adwd [3 months ago] componentInstanceinjector もとれる・・・神・・・ 速く知りたかった 😂

つまり、ng.probe($0).componentInstance.contextでマウントされたコンポーネントが取得できる…

laco [7:04 PM] prettierはコードをキレイにするツールというよりはコードフォーマットについて人間が考えることをやめるためのツールだと思ってるので、prettier後のコードが見づらいと思うのは自分の方を矯正してます

okunokentaro [9:06 AM] prettierは詰め込めるだけ1行に詰め込むクセがあるので、そこだけ文字数でバランスとってます。 80,100,120試して100が一番チーム全員の評判よかったので100で通してます。

あとはprettierが読みにくい整形をする場合それはコードのせいであることが多く、デメテルの法則に反してないか(無駄なチェーン)とか、関数のラップが深すぎないか(責務が適切か)などに気付くきっかけにもなるのです。

kou [5:51 PM] 以前 tslint --fix 使ってみたら正規表現の中の全角スペースが半角スペースに修正されていて危ないなと思ったんですが、prettierはそういう危険性ってあったりしますかね? 今のところ問題無いですが

laco [5:52 PM] tslintそんなことやってたんですか…

kou [6:06 PM] 今試したらやっぱり全角スペースが半角スペースになってます。 no-irregular-whitespace の対象になっているようです。

+ /^([\u30A0-\u30FF]| | )+$/```

kou [4 months ago] https://github.com/angular/material2/blob/master/src/lib/card/card.scss#L6

他のcssのclassも24pxのpaddingを前提にしてstyleされているので、変更できないのではないかと思います。

基本的にAngularMaterialのコンポーネントはcssの上書きを想定していないので、カスタムなデザインが必要な場合は自作した方がいいと思います。

okunokentaro [8:46 PM] 最近は再利用しないコンポーネントはinput使わないようになりました。全部serviceの共有。

akai [10:13 AM] app.module.tsでは基本的にコンポーネントの読み込みやサービスの読み込みは行っていません。

それぞれのドメインごと(大抵第一回層のルーティング単位)にモジュールを作成して、モジュールだけを読み込む、というようになっています。 (edited)

teatwo [12:49 PM] 公式のsharedやcoreの話は、大命題として main.bundle.jsの削減 を上げてると思うんですよね。なので、実は sharedModuleにしてapp.moduleで読み込むなら意味が無くって(main.bundle.jsとして1ファイルになってしまうから)、lazyLoadingModuleで読み込めば十分なものを shared にして、どうしてもapp.moduleでつまりapp.componentやapp.routing-moduleで必要なものだけを core にするって方針だと理解しています。

teatwo [12:50 PM] 自分なんかは、まずはshared作ってapp.moduleに置く形から始めて(core無し)、main.bundle.jsの削減を目指し始めてたら、sharedをcoreとsharedに分離するような手順になりました。LightHouseとかのスピードテストするとmain.bundle.jsのデカさが割と致命的なんですよね:sweat_smile:

kou [6:54 PM] ↑のコードであれば同じだと思います。 RouterModuleのところに使い分けの理由が書いてあります。

https://github.com/angular/angular/blob/master/packages/router/src/router_module.ts#L81

例えばRouterModuleの RouterActivatedRoute みたいなサービスはrootのモジュールに一度だけプロバイドされれば良くて、子のモジュールには必要がないので、それを実現するためにforRoot, forChildでprovidersの内容を変えているようです。

自分も、コンポーネントとサービスの両方含む自作モジュールでは、forRoot, forChildでサービスをプロバイドするかどうか分けています。 (edited)

hybs [11:32 AM] Angularでbuildする前にgitのhash値を埋め込んでおきたいと思っているのですが、npmのprebuildスクリプトとかでenvironment.tsを書き換えるとかしか方法はないものでしょうか

laco [11:41 AM]

npmのprebuildスクリプトとかでenvironment.tsを書き換える これがよさそうですね、現状ではアプリケーション中から環境変数などにはアクセスできないので https://qiita.com/TsuyoshiUshio@github/items/4243ee860b217534c0d2 参考になるかも?

hybs [3:03 PM] Componentにて、Inputがundefinedだったらテンプレートを表示させないってことをやろうとしたい時、 ng-contaner で囲って ngIf するのが良いんでしょうか? 他に良い方法ってありますかね?

laco [3:07 PM] おっしゃるとおりng-containerを使うのがシンプルでベターだと思います

saikou9901 [5:37 PM] ふわっとした質問ですが・・・ 大規模だと、ロードについてはモジュール分割してLazyLoadingなどを使用していきますが、そのロードしたモジュール・Provideされたサービスなどがメモリ上に蓄積されていくと思います。 特にサービス上にBehaviorSubjectを貯めて取りまわすスタイルだと、それをクリアするタイミングを考えないといけないなーと思っています。 何かフレームワークレベルのふるまいでそれに対応しているような例などはありますでしょうか? もしくは、適宜クリア処理を挟み込んでいくしかないでしょうか・・・ (edited)

okunokentaro [5:51 PM] 答えになってないですが、その辺の取り回しがつらすぎてlazy諦めた勢です おっしゃってる問題点はよーくわかる。。 teatwo [2:07 PM] (Angularに限らず、pubsubパターンのpublisher側をどう殺すか問題ありますよね)

kou [22 days ago] おそらく要件としては「アプリケーションを開いたときの初回のページかどうかを判定したい」ということだと思いますので、下のようなServiceとGuardの実装を使うのはどうでしょうか?

2. そのServiceを使用して、初回のページ表示の場合、特定のページに移動させるGuardを実装。```
(edited)



kou [22 days ago]
こんなイメージ
```class FirstNavigationService {
isFirstNavigation = true;

constructor(private router: Router) {
> this.router.events
  > .pipe(filter((event) => event instanceof NavigationStart), take(2))
  > .subscribe(() => {}, () => {}, () => (this.isFirstNavigation = false));

} }``` (edited)

okunokentaro [11:11 PM] ディープコピーは、メソッドごとコピーが必要ならそれぞれモデリングして clone()メソッド生やしますし 雑に参照を断ち切りたいだけなら JSON.parse(JSON.stringify(obj))ですね

kou [5:36 PM] Subjectが何個のSubscriberからsubscribeされているか調べる方法ってあったりしますか? 全てのSubscriptionがunsubscribeされたタイミングを取得したいです。

ConnectableObservableのrefCountの値を使うのが良さそうな気はするのですが、外部から取得する方法が分からず…

ご存知の方いましたらお願いします。

quramy [5:55 PM] 適当にdebuggerで止めて見てみると、 obserervers ってとこに、なんかそれっぽいのが生えてるようにも見えますけど、どうでしょうか。 (edited)

ponday [1:11 PM] RxJS 6.0.0-alpha.3で rxjs/createrxjsに統一されたみたいですね

teatwo [2:06 PM] え、こうゆうのもダメになるんですか??

import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operators';

ponday [2:06 PM]
動かないですねー、、、

teatwo [2:07 PM]
先週変えたばっかりなのに;; (edited)

ponday [2:09 PM]
↑のコードだと、
```import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';```
になるかと、、、

teatwo [3:22 PM] @Outputの命名は 主語+過去動詞 にしてますね。 @Output('optionSelected') とか @Output('valueChanged') とかAngularMaterialとかで見かけるスタイルです

laco [1:41 PM] ngrxのcreateSelectorの旨味があんまりわからなくて、毎回 store.select(state => ...) に関数直接書いちゃうんですけど、何か利点あるんですっけ?

teatwo [1:48 PM] あくまで本家reduxの話ですが、通常だとviewはstore全体の購読になっていて、propsで流すものを絞っているだけである(connectでのmapStateToProps関数のこと)。問題点はstore全体を各containerがごっそりsubscribeしていることによるパフォーマンス。そのために、storeからの購読をviewが絞れるようにselectorが登場してましたけど:thinking_face:? (edited)

laco [1:50 PM] なるほど、画面ごととかにstateの中でスコープを切っておくとパフォーマンスの低下を防げる、という感じですか はーん、理屈としてはなんとなく理解できました その範囲の更新だけがemitされるObservableを作成して中間に置くのか

okunokentaro [3:39 PM] Routerなんですが、 navigateを呼んだときに、どんなComponent, どんなRouteでも毎回必ず処理を挿入したくて、 そういうHttpのインターセプトみたいな仕組みってありましたっけ

なければ オレオレRouterServiceが登場してしまう (edited)

quramy [3:44 PM] router.events を subscribeする感じではダメですか? routingをキャンセルしたい、みたいな要件だと使えないのですが。。。

okunokentaro [3:44 PM] あー、outletの外でですか いや、URLに乗ったパラメーターを別で格納する必要があったので、値が引っ張ってこられたらOKなんです どっかで常駐して router.events.subscribe()するのアリですね

mstssk [3:50 PM] 画面遷移時にスクロール位置をリセットしたくて router.events.filter(e => e instanceof NavigationEnd).subscribe() とかやってました (edited)

akai [12:37 PM] guards機構でログインしていないユーザーをログインページに飛ばしています。

直前にユーザーがアクセスしようとしていたURLを取得する方法はありませんか?ログイン後にアクセスしようとしていたページを表示させてあげたいです。 あらかじめ変数で持てば実現できるのですが取り回ししづらいので、、、

@angular/common のLocation.back()だと、アドレスバーにURL直接打たれたときに、打たれたURLに戻ってはくれないみたいでした。 (edited) Routerにhistoryとかあるのかなと思ったのですがなさそうでした。

laco [12:56 PM] canActivateでログインしているかどうかを見て、ログインしてなかったらstate.urlをクエリパラメータに追加してログインページにリダイレクトし、ログイン後にクエリパラメータをみてリダイレクトすればよいのでは? (edited)

laco [11:13 AM] class MyError extends Error のようにErrorクラスを継承したクラスのインスタンスをthrowしたとき、Zone.jsが拾ってネイティブのErrorに詰め直すようなことって行われてますか?catch節の中で err instanceof MyError がfalseになるんですけど、zone.js使ってると避けられない? cc/ @jialipassion

jialipassion [11:16 AM] @laco, zone.jszone.js/dist/zone-errorをimportしたら、この問題を解消できます。ちょっとErrorの性能に影響がありますけど。

laco [11:17 AM] おっ!試してみます。 ほんとだ、instanceofが trueになりました

jialipassion [11:20 AM] そうですね、これをimportしたら、ZoneにかかわるStackTraceもなくなります。でもnew Error()の性能がちょっと下がります。(Error Stack TraceをFilterするため) 次のバージョン?でこのエラー継承とError StackTrace Filterの機能を分けるかもしれません。

laco [11:22 AM] なるほど、NativeのErrorのprototypeを書き換えてるってことですかね エラー発生時のパフォーマンスが多少落ちるのは大きな問題ではないので、入れてみます。

機能を分ける ぜひ!エラー継承だけ欲しいです!

ES2016-2018の復習

https://medium.freecodecamp.org/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e

UXいいねボタン https://qiita.com/ovrmrw/items/bf1691fab3579f0d1075

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment