middleware パターンに対する考察
middlewareとは、ここではexpress等の、requestを受けてresponseを返すような構造の構成要素として使われるものを指す
これは、全体としては入力を受けて、出力を返すだけの関数である。
これを、層状にスライスして、どんどん層をフォールバックさせるような構造にしたものがmiddlewareである
jsfunction donothingMiddleware(req,res,next) {
return next(req)
}
tstype Reducer = (state: State, action: Action) => State
入力が前ステートとアクション
出力が新しいステート
middlewareはこのように書ける。
tsfunction middlewareExample(state:State, action:Action, next:(state:State,action:Action)=>State) {
console.log('before')
const nextState = next(state,action)
console.log('after')
return nextState
}
さて、ここで、入出力の型をちょっと工夫してみよう
まず、nextはReducer型であることに注意したい。
その上で、nextのみを先に引数として受け取る形でカリー化する
tsconst middlewareExample = (next: Reducer) => (state:State, action:Action) => {
console.log('before')
const nextState = next(state,action)
console.log('after')
return nextState
}
そうすると、nextを受け取り引数をひとつ受け取った後に返す型が、これもまたReducerであることが分かる
tsconst middlewareExample: (next:Reducer) => Reducer = (next) => (state, action) => {
console.log('before')
const nextState = next(state,action)
console.log('after')
return nextState
}
これを改めて、Middleware型としよう
tstype Middleware = (next:Reducer) => Reducer
const middlewareExample: Middleware = (next) => (state, action) => {
console.log('before')
const nextState = next(state,action)
console.log('after')
return nextState
}
さて、この形のmiddlewareを複数結合するにはどうすればいいか?
console.logしたり、型をみながらイジるとわかるが、こんな感じだ
tsfunction combine(...middlewares: Middleware[]): Middleware {
return (initialReducer) =>
middlewares.reduceRight(
(next, middleware) => middleware(next),
initialReducer
);
}
複数のmiddlewareを繋げたら、ひとつのmiddlewareになることを言っている
tsconst passthrough =
(s: string): Middleware =>
(next) =>
(state, action) => {
console.log("start", s);
const nextState = next(state, action);
console.log("end", s);
return nextState;
};
const m = combine(
passthrough("1"),
passthrough("2"),
passthrough("3"),
passthrough("4")
);
m((state) => {
console.log("端っこです");
return state;
})(initialState, action);
logstart 1
start 2
start 3
start 4
端っこです
end 4
end 3
end 2
end 1
こういう感じになる
middlewareの配列自体もreduceしてるから、頭がこんがらがるかも
いざ作ってみたが、次のstateを求めるためのreducerをmiddleware的に、後続処理を扱えるようにするメリットを、現状まだ遭遇できてない
後続処理がなにか選択範囲を求めるようなもので、それに対する操作をしたいときとか?
そういうときも、選択と操作は別のアクションで各々実行されるからなあ
早期returnは必要であるのは分かる
なにか後続の処理が大きいステートを指していて、そのステートが抜けて親に任せるケースとかには使えるかも