StateMachineStore と再利用可能な設定¶
Monaka では、大規模なマシンを構造化するパターンとして StateMachineStore(DSL をインラインで持つ名前付きクラス)と stateMachine { }(再利用可能な設定値)の2つを提供しています。どちらも同じランタイム動作を生成しますが、違いはコードの整理方法にあります。
store { } — インライン匿名マシン¶
val store = store<CounterState, CounterAction, CounterEffect>(viewModelScope) {
initialState(CounterState(0))
state<CounterState> {
on<CounterAction.Increment> { transition(state.copy(count = state.count + 1)) }
}
}
stateMachine { } — 再利用可能な設定¶
stateMachine { } は、起動せずにイミュータブルな StateMachine のスナップショットを構築します。
val loginMachineConfig = stateMachine<LoginState, LoginAction, LoginEffect> {
initialState(LoginState.Idle)
state<LoginState.Idle> { … }
state<LoginState.Typing> { … }
}
val store1 = store(loginMachineConfig, scope1)
val store2 = store(loginMachineConfig, scope2, initialState = LoginState.Typing("bob"))
非同期による状態の復元¶
val store = store(
stateMachine = loginMachineConfig,
scope = viewModelScope,
initializer = { dataStore.data.first().toLoginState() },
) {
initialState(LoginState.Idle)
}
StateMachine の委譲 — 依存関係を注入する名前付きクラス¶
class LoginStateMachine(
loginRepository: LoginRepository,
) : StateMachine<LoginState, LoginAction, LoginEffect> by stateMachine(builder = {
initialState(LoginState.Idle)
state<LoginState.Typing> {
on<LoginAction.Submit> {
task("login", autoCancel = true) {
when (val result = loginRepository.login(state.username, state.password)) {
is Success -> dispatch(LoginAction.LoginSucceeded(result.user))
is Failure -> dispatch(LoginAction.LoginFailed(result.message))
}
}
transition(LoginState.Submitting)
}
}
state<LoginState> {
on<LoginAction.Logout> {
transition(LoginState.Idle)
sideEffect(LoginEffect.NavigateToLogin)
}
}
install(LoggingPlugin(tag = "Login"))
})
パターンの選択¶
| パターン | 適用場面 |
|---|---|
store { } インライン |
小規模マシン、テスト不要、または ViewModel 内にマシンが完結する場合。 |
stateMachine { } + store(config, scope) |
再利用可能な設定値が必要な場合 — 複数インスタンス、testStore への受け渡し、設定と起動の分離。 |
名前付きクラス : StateMachine<> by stateMachine { } |
依存関係を注入する必要があり、testStore にも直接渡せる名前付き型クラスが必要な場合。 |