Store

class Store<S : State, A : Action>(reducer: Reducer<S, A>, initialState: S, coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, coroutineScopeName: String = DEFAULT_COROUTINE_SCOPE_NAME, effectIdSource: EffectIdSource = UUIDEffectIdSource(), logger: Logger = NoopLogger(), exceptionHandler: ExceptionHandler = ThrowingExceptionHandler, parentJob: Job? = null) : AutoCloseable

A construct for managing complex state safely in concurrent environments.

An Effect can have an owner. This allows to relate an effect to something else. E.g. this might be UI that wants to display a loading state for an effect as long as it's running, and display the result when it's done.

An owner can cease to exist while the Effect is running, reappear at a later time, and then handle the result of its effect. The Store does not have references to the owners of effects, which allows for owners to disappear and reappear without having to handle references as with Listeners. This eases handling scenarios like Activity deaths on Android, where the UI that triggered an action can disappear completely - Objects garbage collected - and reappear at a later time wanting to display the current state of the action. The screen can be set as owner using a persistent id and a new/resurrected instance can find the data again by that ownerId.

Because the Store doesn't know anything about effect owners, it can't clean up effect results of effects with owners by itself, as it doesn't know when an owner has handled a result. The potential "disappearance" of an owner does not imply that an effect state is not needed anymore, as it might reappear later. Therefore, to clean up an EffectState and free the memory, an owner has to do so explicitly by removing it via an Action.

If an owner does not remove its EffectState from the effect states, then it will stay there indefinitely, using up memory. Ergo when an owner ceases to exist completely and does not reappear, then its effect states continue to exist. This is a known - and currently accepted - limitation of this approach.

Why are ownerIds needed, when effectIds exist? An owner dispatches an action to trigger an effect. This action lands in a queue and is processed a bit later. Only then is the effectId created. If the owner disappeared before, then it can't know the effectId of the effect it triggered. By passing an ownerId with the action, the id can be passed to the effect. Even if the owner disappeared in the meantime, it can later identify its effects by the ownerId.

The Reducer is run atomically, meaning state updates are thread-safe.

Parameters

reducer

The Reducer that will handle Actions and determine state updates and reactions.

initialState

The initial state of the Store.

coroutineDispatcher

The CoroutineDispatcher all operations run on. Reducer, effects, and internal operations run on this dispatcher. Default value is Dispatchers.Default.

coroutineScopeName

The name for the CoroutineScope the Store uses. Useful for debugging. The default is DEFAULT_COROUTINE_SCOPE_NAME.

logger

A logger that logs received actions and state updates. Useful for debugging. The default is NoopLogger.

exceptionHandler

The ExceptionHandler that is installed in the CoroutineScope of the Store. All uncaught exceptions occurring in the reducer and effects are passed to this handler. The default is ThrowingExceptionHandler which rethrows all received exceptions.

parentJob

An optional Job that, when given, is used as the parent for the Job the Stores CoroutineScope uses. When the parent Job is canceled, then the Stores CoroutineScope is canceled together with all its running Effects.

Constructors

Link copied to clipboard
constructor(reducer: Reducer<S, A>, initialState: S, coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, coroutineScopeName: String = DEFAULT_COROUTINE_SCOPE_NAME, effectIdSource: EffectIdSource = UUIDEffectIdSource(), logger: Logger = NoopLogger(), exceptionHandler: ExceptionHandler = ThrowingExceptionHandler, parentJob: Job? = null)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
val stateFlow: StateFlow<S>

Functions

Link copied to clipboard
open override fun close()
Link copied to clipboard
fun dispatch(action: A)
Link copied to clipboard
fun shutdown()

Cancels the Stores CoroutineScope. This cancels all running Effects and prevents any new Actions from being processed. Dispatching actions after shutdown has no effect. The Store can not be restarted.