redux-aggregate が v2 になりました。主な変更点は以下の通りで、v1 からの Breaking Change はありません。アプリケーションスケールアップの際に効果が期待出来る変更を追加しています。
- createActions の追加
- Aggregate.subscribe の追加
- subscriptions 概念の追加
これからは createAggregate で生成された ボイラープレートでも、reducer / action が多対多の関係を持つことが可能になります。もちろん新しく追加されたAPIも、必要最小限の型定義から推論型を導出します。
createActions
従来の ActionCreator 定義を確認してみましょう。ActionType と payload が結合していますね。
function tick (message) {
const date = new Date()
if (message !== undefined) return { type: 'TIMER_TICK', pyload: { date }}
return { type: 'TIMER_TICK', payload: { date, message }}
}
ここから ActionType を取り除き、payload だけを return するものを actionSrc と呼んでいます。in/out があるだけのただの純関数であることが分かります。
// @ ActionSources
function tick (message) {
const date = new Date()
if (message !== undefined) return { date }
return { date, message }
}
export const TimerAC = { tick }
createAggregate
と同様に、ActionSources / namespace を付与し createActions
を call します。
import { createActions } from 'redux-aggregate'
import { ActionSources } from 'path/to/actions/timer'
const Timer = createActions(ActionSources, 'timer/')
const {
types, // ActionTypes
creators, // ActionCreators
} = Timer
createActions
では reducerFactory は生成されません。
Aggregate.subscribe
createAggregate で生成された Aggregate インスタンスに、subscribe というメソッドが生えました。このメソッドで上述の Actions を購読します。購読に応じた変更処理は後述の subscriptions に書いていきます。
import { TimerActions } from 'path/to/actions/timer'
import { TodosMutations, TodosSubscriptions } from 'path/to/models/todos'
export const Timer = createActions(TimerActions, 'timer/')
export const Todos = createAggregate(TodosMutations, 'todos/')
Todos.subscribe(
Timer, // Actions or Aggregate
TodosSubscriptions.Timer // Subscriptions
)
一般的な subscribe とは少し異なり、これは reducerFactory を拡張するものです(うっかり多重 subscribe したところで、違いはありません)。
subscriptions
mutations が定義されているモデルファイルに、購読マップ(subscriptions)を定義します。見た目は mutations とそっくりですが、ActionType / ActionCreator を生成しません。subscription 単体は、従来の switch文で選り分けられた先の reducer と等価です。
注意しなければいけないのは、subscription は規約を守らなければ、型エラーが発生します。一体何の規約かというと、それは開発者自身が定義したメソッド名・payload に則るということです。
example で示しているとおり、TimerActions の tick というメソッド名・メソッド戻り値を第2引数にとることが subscription関数に期待されます。第1引数は TodosState
を指します。modelファイルに定義される全関数は、所属する state を中心に回ります。
// subscription の名称は tick に制約される
function tick (message) {
const date = new Date()
if (message !== undefined) return { date }
return { date, message }
}
// subscription の第二引数は { date } か { date, message } に制約される
const TodosSubscriptions = {
Timer: {
tick(state, { date, message }) {
if (message !== undefined) {
return { ...state, date, messageFromTimer: message }
}
return { ...state, date }
}
}
}
他のメディアでも発信していますが、このライブラリの最大の利点は、**「アプリケーションの核が純粋な言語仕様のみで確立されていること・ Redux の概念が介入していないこと」**であり、コード・付随テストの寿命を伸ばす大事なことだと筆者は考えています。
利用シーン
概念が増えたことで少しややこしくなりましたが、今回の追加機能は必要に応じて導入、スケールアップのシーンで役立てて下さい。はじめは 単体Aggregte の関心事であった Action が、他のAggregate の関心事となったタイミングで利用すると良いかと思います。
Aggregate.subscribe は 他Aggregate の Action も購読出来ます。2つのAggregate の関心ごとになった際には、下流が上流を購読すれば、それで十分です。それより多くの関心事となった場合や、当初から多くの関心事であると想定された場合は、createActions
を使って「Action のみの集約」を作ってください。
Action を全く持つ必要のない集約の場合、createSubscriber
を使って「Reducer のみの集約」を作ってください。example ->
公式Document を公開しました https://redux-aggregate.js.org/