is it possible to call dispatch while inside mapEventToState
see my example below, I had to comment out dispatches and replace them with yields,
however I would really prefer not to do it, as AuthSuccessEvent and AuthFailureEvent cases are already described in switch statement, and it feels like code-repetition, and violation of single responsibility.
btw, this is perfectly allowed in redux middlewares like saga or thunk
@override
Stream<AuthState> mapEventToState(
AuthState currentState, AuthEvent event) async* {
switch (event.runtimeType) {
case AuthSuccessEvent:
final success = (event as AuthSuccessEvent);
yield AuthState(user: success.user, token: success.token);
break;
case AuthFailureEvent:
final error = (event as AuthFailureEvent).error;
yield AuthState(error: error);
break;
case AuthenticateEvent:
final auth = (event as AuthenticateEvent);
yield AuthState(loading: true);
try {
final token = await AuthService.authenticate(auth.user);
yield AuthState(user: auth.user, token: token.token);
// dispatch(AuthSuccessEvent(auth.user, token.token));
} catch (e) {
yield AuthState(error: e.toString());
// dispatch(AuthFailureEvent(e.toString()));
}
break;
default:
yield currentState;
}
@venil7 thanks for opening this! Are you able to share the full source code with me?
@felangel not really, but snippet above should give enough context.
everything else is some arbitrary code unrelated to issue.
@venil7 sounds good I'll try to reproduce your issue. One thing I'm immediately noticing is you're switching on runtimeType.
Runtime type isn't something I'd use for program logic. It changes based on compiler options, and actually even using it can make optimizations for your app much harder.
I would recommend adding your own type implementation to your events if you want to be able to use a switch statement or using an enum to represent the events and states if they are simple enough.
import 'dart:async';
import 'package:bloc/bloc.dart';
enum AuthEvent { authenticate, authenticated }
enum AuthState { initial, success, failure, loading, done }
class AuthBloc extends Bloc<AuthEvent, AuthState> {
@override
AuthState get initialState => AuthState.initial;
@override
void onTransition(Transition<AuthEvent, AuthState> transition) {
print(transition);
}
@override
Stream<AuthState> mapEventToState(
AuthState currentState,
AuthEvent event,
) async* {
switch (event) {
case AuthEvent.authenticate:
yield AuthState.loading;
await Future.delayed(Duration(milliseconds: 100), null);
yield AuthState.success;
dispatch(AuthEvent.authenticated);
break;
case AuthEvent.authenticated:
yield AuthState.done;
break;
}
}
}
main() {
final bloc = AuthBloc();
bloc.dispatch(AuthEvent.authenticate);
}
Outputs:
Transition {
currentState: AuthState.initial,
event: AuthEvent.authenticate,
nextState: AuthState.loading
}
Transition {
currentState: AuthState.loading,
event: AuthEvent.authenticate,
nextState: AuthState.success
}
Transition {
currentState: AuthState.success,
event: AuthEvent.authenticated,
nextState: AuthState.done
}
Which seems to working just fine. I'd need to look at the exact code to pin-point the problem you're having. I'd suggest adding an onError override in your bloc to make sure no exceptions are being thrown because it seems like the issue you're having is not directly due to the dispatch.
Let me know if that helps.
Closing this for now but feel free to comment if you have any additional comments/questions. 馃憤
Most helpful comment
Outputs:
Which seems to working just fine. I'd need to look at the exact code to pin-point the problem you're having. I'd suggest adding an
onErroroverride in your bloc to make sure no exceptions are being thrown because it seems like the issue you're having is not directly due to the dispatch.Let me know if that helps.