How to dispatch multiple async actions that are based on the result of another async action?

Issue

Once my user login is successful and I have a Firebase User I want to check if there is already a Profile created on my server by dispatching an action which makes an async call to get a Profile.

If no Profile is returned I want to navigate the app to Profile creation page otherwise, load the Profile page.

I’m thinking dispatching the second action right in the login middleware, but it seems weird to be mixing profile and login code in the login middleware.

Is there a better way or more of a standard way to do what I’m trying to do?

I haven’t tried anything, but I was thinking of dispatching the second action in the login middleware and navigating to the desired page from there.

ThunkAction<AppState> logIn = (Store<AppState> store) async {
  store.dispatch(UserLoginAction());

  try {
    final GoogleSignIn _googleSignIn = new GoogleSignIn();
    GoogleSignInAccount googleUser = await _googleSignIn.signIn();
    GoogleSignInAuthentication googleAuth = await googleUser.authentication;

    final FirebaseAuth _auth = FirebaseAuth.instance;
    final FirebaseUser _fb = await _auth.signInWithGoogle(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    User user = new User(id: _fb.uid, name: _fb.displayName, email: _fb.email, photoUrl: _fb.photoUrl);
    store.dispatch(UserLoginSuccessAction(user: user));

    store.dispatch(GetProfileAction());

    Profile profile = await ProfileService.get().getProfile(_fb.uid);

    store.dispatch(GetProfileSuccessAction(profile: profile));

    if (profile) {
      // Navigate to Profile Page
    } else {
      // Navigate to Profile Creation Page
    }

  } catch(error) {
    store.dispatch(UserLoginFailAction(error: error));
  }
};

Edit:
I just thought of a way which seems better than my previous suggestion.

What if after successful login I just navigate them to the Profile page. On the init of the Profile page is where I would dispatch the action to get the Profile. The Profile page would render differently based on whether the Profile was initialized in the AppState after the middleware and reducer have completed.

Does this seem okay to do or is there a different and better way?

Solution

Dispatch GetProfileAction() on ProfilePage’s onInit.

Your second suggested solution is more appropriate for these reasons:

  1. Redux encourages a complete separation between features. Each different feature should be written as distinct as possible: you don’t want to mix actions belong to login with those belong to profile. This what makes apps written in Redux architecture very maintainable, where shifting or replacing features could be done easily. Imagine if one day your app decided to remove the login feature. This code separation would significantly minimize the changes required.
  2. Use the main trigger to dispatch an action. Before dispatching an action, you should ask yourself what is the primary reason to launch this action? In this case, would that be the successful login or the profile page initialization that primarily triggers to get the user profile? I personally think that we should always dispatch get the user’s profile on the profile page load as this page is completely dependent on the data retrieved from this action. Think of this way, what happens if Profile page is initialized for some reasons other than login? Your app will end up forcing to build the Profile Page although the profile data is not available in the state.
  3. Reusable as a Refresh action. I assume that user’s profile data keeps changing and need constant update. This should be done at a minimum on reloading or reinitializing the Profile page. You don’t expect users to logout, and to login back into the app to dispatch the get user’s profile data, do you? Yes, you can dispatch the same action at multiple different places, but keeping it minimum would be ideal.

Answered By – Michael Yuwono

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *