eventInterceptor<Context> method

System<State, Event> eventInterceptor<Context>({
  1. required Context createContext(),
  2. ContextEffect<Context, State, Event>? updateContext,
  3. required InterceptorWithContext<Context, Event> interceptor,
  4. void dispose(
    1. Context context
    )?,
})

An interceptor that can intercept event.

This is a low level operator which can be used for supporting high level operators like system.ignoreEvent and system.debounceOn.

API Overview

The key point for this operator is, we are associating a custom Context with it:


class SomeContext { ... }

...

system
 .eventInterceptor<SomeContext>(
   createContext: () => SomeContext(), // create context here
   updateContext: (context, state, oldState, event, dispatch) {
     // update context here if needed.
   },
   interceptor: (context, dispatch, event) {
     // intercept event base on the context,
     // call `dispatch(event);` if we pass the event,
     // don't call `dispatch(event);` if we ignore the event.
   },
   dispose: (context) {
     // dispose the context if needed.
   }
 )
 ...

Usage Example

Below code shown how to implement high level system.ignoreEvent based on low level system.eventInterceptor:

class _IgnoreEventContext<State> {
  late State state;
}

extension FilterEventOperators<State, Event> on System<State, Event> {

  ...

  /// Ignore event based on current state and candidate event.
  System<State, Event> ignoreEvent({
    required bool Function(State state, Event event) when
  }) {
    final test = when;
    return eventInterceptor<_IgnoreEventContext<State>>( //  <-- call `this.eventInterceptor`
      createContext: () => _IgnoreEventContext(),
      updateContext: (context, state, oldState, event, dispatch) {
        context.state = state; // cache current state in context
      },
      interceptor: (context, dispatch, event) {
        final shouldIgnoreEvent = test(context.state, event);
        if (!shouldIgnoreEvent) {
          dispatch(event);
        }
      },
    );
  }
}

Usage of system.ignoreEvent:

futureSystem
  .ignoreEvent(
    when: (state, event) => event is TriggerLoadData && state.loading
  ) 
  ...

Above code shown if the system is already in loading status, then upcoming TriggerLoadData event will be ignored.

We can treat system.ignoreEvent as a special case of system.eventInterceptor, As an analogy, if we say system.ignoreEvent is a square, then system.eventInterceptor is a rectangle.

Implementation

System<State, Event> eventInterceptor<Context>({
  required Context Function() createContext,
  ContextEffect<Context, State, Event>? updateContext,
  required InterceptorWithContext<Context, Event> interceptor,
  void Function(Context context)? dispose,
}) {
  return runWithContext<Context>(
    createContext: createContext,
    run: (context, run, nextReduce, nextEffect, nextInterceptor) {
      bool isDisposed = false;
      final Effect<State, Event>? localEffect = updateContext == null ? null : (state, oldState, event, dispatch) {
        updateContext(context, state, oldState, event, dispatch);
      };
      Dispatch<Event> localInterceptor(Dispatch<Event> dispatch) => Dispatch((event) {
        if (isDisposed) return;
        interceptor(context, dispatch, event);
      });
      final sourceDisposer = run(
        reduce: nextReduce,
        effect: combineEffect(localEffect, nextEffect),
        interceptor: combineInterceptor(localInterceptor, nextInterceptor),
      );
      return Disposer(() {
        isDisposed = true;
        sourceDisposer();
      });
    },
    dispose: dispose,
  );
}