JavaScript
TypeScript
RxJS
redux
redux-observable

redux-observableで作ったintervalのobservableをHot Module Replacementで止まるようにする

epicでintervalを利用するような場合、HMRが効いてるとそのまま動いてしまう。

storeはHMRで更新されるので影響は無いが、consoleなどで追っかけているとObserverが動いていてちょっと気持ち悪い


解決方法: Subjectを利用してmodule.hot.disposeで発火して止める

import { combineEpics } from "redux-observable"

import { interval, Subject } from "rxjs"
import { map, takeUntil } from "rxjs/operators"

const disposer = new Subject()

export const timerEpic = () => {
return interval(1000).pipe(
takeUntil(disposer), // disposerが動くまでtimerを動かす
map((time) => ({
type: "TIMER",
value: time
}))
)
}

if (module.hot) {
module.hot.dispose((data) => {
disposer.next()
})
}

typescriptの場合、module.hot@types/webpack-envに定義されているので入れると良い


HMR部分を分離する

こんな感じにHot Reload関連の部分を切り出しておくのも良いだろう。

// hotReload.ts

import { Subject } from "rxjs"
import { takeUntil } from "rxjs/operators"

const hmrDisposer = new Subject()

export const registerDisposeHandler = (module) => {
if (module.hot) {
module.hot.dispose(() => hmrDisposer.next())
}
}

export const takeUntilHotReload = () => takeUntil(hmrDisposer)

ただしmodule.hotのフックは全てのdependencyのルートであるrootEpicの記述箇所と一緒にしておかないと上手く発火されないので注意

export const rootEpic = combineEpics(pingEpic)

// 全てのepicが更新されたタイミングでdisposeを呼び出すように仕掛ける
registerDisposeHandler(module)