forEachMap<GlobalState, GlobalAction, GlobalEnvironment, ID> method

Reducer<GlobalState, GlobalAction, GlobalEnvironment> forEachMap<GlobalState, GlobalAction, GlobalEnvironment, ID>({
  1. required Map<ID, State> toMapState(
    1. GlobalState
    ),
  2. required GlobalState toGlobalState(
    1. GlobalState,
    2. Map<ID, State>
    ),
  3. required (ID, Action)? toLocalAction(
    1. GlobalAction
    ),
  4. required GlobalAction toGlobalAction(
    1. ID,
    2. Action
    ),
  5. required Environment toLocalEnvironment(
    1. ID,
    2. GlobalEnvironment
    ),
})

Like forEach, but operates on a Map<ID, State> instead of an Iterable<State>.

Implementation

Reducer<GlobalState, GlobalAction, GlobalEnvironment> forEachMap<GlobalState, GlobalAction, GlobalEnvironment, ID>({
  required Map<ID, State> Function(GlobalState) toMapState,
  required GlobalState Function(GlobalState, Map<ID, State>) toGlobalState,
  required (ID, Action)? Function(GlobalAction) toLocalAction,
  required GlobalAction Function(ID, Action) toGlobalAction,
  required Environment Function(ID, GlobalEnvironment) toLocalEnvironment,
}) {
  return Reducer<GlobalState, GlobalAction, GlobalEnvironment>(
    reduce: (state, action, environment) {
      final extracted = toLocalAction(action);
      if (extracted == null) {
        return (state: state, effect: Effect.none());
      }
      final (id, localAction) = extracted;
      final map = toMapState(state);
      final localState = map[id];
      assert(localState != null, """
      A "forEach" received an action for a missing element. …

        ID: $id
        Action: $localAction

      This is generally considered an application logic error, and can happen for a few reasons:

      • A parent reducer removed an element with this ID before this reducer ran. This reducer
      must run before any other reducer removes an element, which ensures that element reducers
      can handle their actions while their state is still available.

      • An in-flight effect emitted this action when state contained no element at this ID.
      While it may be perfectly reasonable to ignore this action, consider canceling the
      associated effect before an element is removed, especially if it is a long-living effect.

      • This action was sent to the store while its state contained no element at this ID. To
      fix this make sure that actions for this reducer can only be sent from a view store when
      its state contains an element at this id. In SwiftUI applications, use "ForEachStore".
      """);
      final (state: newLocalState, effect: effect) = reduce(
        // ignore: null_check_on_nullable_type_parameter
        localState!,
        localAction,
        toLocalEnvironment(id, environment),
      );
      return (
        state: toGlobalState(
          state,
          map.map(
            (key, value) => MapEntry(key, key == id ? newLocalState : value),
          ),
        ),
        effect: effect.map((e) => toGlobalAction(id, e)),
      );
    },
  );
}