Skip to content

Commit

Permalink
Refactor transformStates to transformTransitions
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel committed Feb 3, 2020
1 parent 068300d commit 61c7a01
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 49 deletions.
100 changes: 57 additions & 43 deletions packages/bloc/lib/src/bloc.dart
Expand Up @@ -4,6 +4,11 @@ import 'package:meta/meta.dart';

import '../bloc.dart';

/// Signature for a mapper function which takes an [Event] as input
/// and outputs a [Stream] of [Transition] objects.
typedef TransitionFunction<Event, State> = Stream<Transition<Event, State>>
Function(Event);

/// {@template bloc}
/// Takes a `Stream` of `Events` as input
/// and transforms them into a `Stream` of `States` as output.
Expand Down Expand Up @@ -41,14 +46,19 @@ abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> {
void Function() onDone,
bool cancelOnError,
}) {
return _buildStateStream().listen(
return _createStateStream().listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}

Stream<State> _createStateStream() async* {
yield state;
yield* _stateController.stream;
}

/// Called whenever an [event] is [add]ed to the [bloc].
/// A great spot to add logging/analytics at the individual [bloc] level.
void onEvent(Event event) => null;
Expand Down Expand Up @@ -97,10 +107,10 @@ abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> {
await _stateController.close();
}

/// Transforms the [events] stream along with a [next] function into
/// a `Stream<State>`.
/// Transforms the [events] stream along with a [transitionFn] function into
/// a `Stream<Transition>`.
/// Events that should be processed by [mapEventToState] need to be passed to
/// [next].
/// [transitionFn].
/// By default `asyncExpand` is used to ensure all [events] are processed in
/// the order in which they are received.
/// You can override [transformEvents] for advanced usage in order to
Expand All @@ -112,26 +122,28 @@ abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> {
///
/// ```dart
/// @override
/// Stream<State> transformEvents(events, next) => events.switchMap(next);
/// Stream<Transition<Event, State>> transformEvents(events, transitionFn) {
/// return events.switchMap(transitionFn);
/// }
/// ```
///
/// Alternatively, if you only want [mapEventToState] to be called for
/// distinct [events]:
///
/// ```dart
/// @override
/// Stream<State> transformEvents(events, next) {
/// Stream<Transition<Event, State>> transformEvents(events, transitionFn) {
/// return super.transformEvents(
/// events.distinct(),
/// next,
/// transitionFn,
/// );
/// }
/// ```
Stream<State> transformEvents(
Stream<Transition<Event, State>> transformEvents(
Stream<Event> events,
Stream<State> Function(Event) next,
TransitionFunction<Event, State> transitionFn,
) {
return events.asyncExpand(next);
return events.asyncExpand(transitionFn);
}

/// Must be implemented when a class extends [bloc].
Expand All @@ -141,51 +153,53 @@ abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> {
/// and return the new [state] in the form of a `Stream<State>`.
Stream<State> mapEventToState(Event event);

/// Transforms the `Stream<State>` into a new `Stream<State>`.
/// By default [transformStates] returns the incoming `Stream<State>`.
/// You can override [transformStates] for advanced usage in order to
/// Transforms the `Stream<Transition>` into a new `Stream<Transition>`.
/// By default [transformTransitions] returns
/// the incoming `Stream<Transition>`.
/// You can override [transformTransitions] for advanced usage in order to
/// manipulate the frequency and specificity at which `transitions`
/// (state changes) occur.
///
/// For example, if you want to debounce outgoing [states]:
/// For example, if you want to debounce outgoing state changes:
///
/// ```dart
/// @override
/// Stream<State> transformStates(Stream<State> states) {
/// return states.debounceTime(Duration(seconds: 1));
/// Stream<Transition<Event, State>> transformTransitions(
/// Stream<Transition<Event, State>> transitions,
/// ) {
/// return transitions.debounceTime(Duration(seconds: 1));
/// }
/// ```
Stream<State> transformStates(Stream<State> states) => states;

Stream<State> _buildStateStream() async* {
yield _state;
yield* _stateController.stream;
Stream<Transition<Event, State>> transformTransitions(
Stream<Transition<Event, State>> transitions,
) {
return transitions;
}

void _bindEventsToStates() {
Event currentEvent;

transformStates(transformEvents(_eventController.stream, (event) {
currentEvent = event;
return mapEventToState(currentEvent).handleError(_handleError);
})).forEach(
(nextState) {
if (state == nextState || _stateController.isClosed) return;
final transition = Transition(
currentState: state,
event: currentEvent,
nextState: nextState,
);
try {
BlocSupervisor.delegate.onTransition(this, transition);
onTransition(transition);
_state = nextState;
_stateController.add(nextState);
} on dynamic catch (error) {
_handleError(error);
}
transformTransitions(transformEvents(
_eventController.stream,
(event) {
return mapEventToState(event).map((nextState) {
return Transition(
currentState: state,
event: event,
nextState: nextState,
);
}).skipWhile((transition) {
return state == transition.nextState || _stateController.isClosed;
}).handleError(_handleError);
},
);
)).forEach((transition) {
try {
BlocSupervisor.delegate.onTransition(this, transition);
onTransition(transition);
_state = transition.nextState;
_stateController.add(transition.nextState);
} on dynamic catch (error) {
_handleError(error);
}
});
}

void _handleError(Object error, [StackTrace stacktrace]) {
Expand Down
1 change: 0 additions & 1 deletion packages/bloc/pubspec.yaml
Expand Up @@ -17,4 +17,3 @@ dev_dependencies:
mockito: ^4.0.0
effective_dart: ^1.2.0
rxdart: ^0.23.0

12 changes: 7 additions & 5 deletions packages/bloc/test/helpers/complex/complex_bloc.dart
Expand Up @@ -9,11 +9,11 @@ class ComplexBloc extends Bloc<ComplexEvent, ComplexState> {
ComplexState get initialState => ComplexStateA();

@override
Stream<ComplexState> transformEvents(
Stream<Transition<ComplexEvent, ComplexState>> transformEvents(
Stream<ComplexEvent> events,
Function(ComplexEvent) next,
TransitionFunction<ComplexEvent, ComplexState> transitionFn,
) {
return events.switchMap(next);
return events.switchMap(transitionFn);
}

@override
Expand All @@ -32,7 +32,9 @@ class ComplexBloc extends Bloc<ComplexEvent, ComplexState> {
}

@override
Stream<ComplexState> transformStates(Stream<ComplexState> states) {
return states.debounceTime(Duration(milliseconds: 50));
Stream<Transition<ComplexEvent, ComplexState>> transformTransitions(
Stream<Transition<ComplexEvent, ComplexState>> transitions,
) {
return transitions.debounceTime(Duration(milliseconds: 50));
}
}

0 comments on commit 61c7a01

Please sign in to comment.