Combine reducers in Flutter

1,449

The action that you dispatch will be sent to all the reducers. Using checks you can mutate your state easily. This will also prevent unnecessary updates in the state.

Very soon you will have multiple actions being dispatched & handling them as follows will allow you to do that.

Your signUpReducer should look something like this:

signUpReducer(SignUpState prevState, dynamic action) {

  if (action is SetSignUpStateAction) {
    return setSignUpState(prevState, action);
  }
  return prevState;
}

SignUpState setSignUpState(SignUpState prevState, SetSignUpStateAction action) {
    final payload = action.signUpState;
    print(action); // prints Instance of 'SetSignUpStateAction'
    print(prevState); // prints Instance of 'SignUpState'
    print("signUpReducer");
    return prevState.copyWith(
      isError: payload.isError,
      isLoading: payload.isLoading,
      isLoggedIn: payload.isLoggedIn,
    );
}

Your loginReducer should look something like this:

loginReducer(LoginState prevState, dynamic action) {

  if(action is SetLoginStateAction) {
    return setLoginState(prevState, action);
  }
  return prevState;
}

LoginState setLoginState(LoginState prevState, SetLoginStateAction action) {
    final payload = action.loginState;

    return prevState.copyWith(
      isError: payload.isError,
      isLoading: payload.isLoading,
    );
}
Share:
1,449
user3808307
Author by

user3808307

Updated on December 01, 2022

Comments

  • user3808307
    user3808307 over 1 year

    I am using Redux with Flutter with this library. If the solution is to change to redux dart library then I will change it.

    This is the code I have for the store, I have been taking parts from different tutorials

    @immutable
    class AppState {
      final SignUpState signUpState;
      final LoginState loginState;
    
      AppState({
        @required this.signUpState,
        @required this.loginState,
      });
      AppState copyWith({
        SignUpState signUpState,
        LoginState loginState,
      }) {
        return AppState(
          signUpState: signUpState ?? this.signUpState,
          loginState: loginState ?? this.loginState,
        );
      }
    }
    
    AppState appReducer(AppState state, action) {  
      return AppState(
        signUpState: signUpReducer(state.signUpState, action),
        // loginState: loginReducer(state.loginState, action),
      );
    }
    
    class Redux {
      static Store<AppState> _store;
    
      static Store<AppState> get store {
        if (_store == null) {
          throw Exception("store is not initialized");
        } else {
          return _store;
        }
      }
    
      static Future<void> init() async {
        final signUpStateInitial = SignUpState.initial();
        final loginStateInitial = LoginState.initial();
    
        _store = Store<AppState>(
          appReducer,
          middleware: [thunkMiddleware, new LoggingMiddleware.printer()],
          initialState: AppState(
              signUpState: signUpStateInitial,
              loginState: loginStateInitial,
          ),
        );
      }
    }
    

    I am testing first with signUpState, which currently works when I only use that one, the moment I add another reducer and uncomment the line

    // loginState: loginReducer(state.loginState, action),
    

    on trying to dispatch a signUp related Action that does work with the above line commented I get Uncaught (in promise) Error: Expected a value of type 'SetLoginStateAction', but got one of type 'SetSignUpStateAction'

    I thought this part of the code was combining the reducers:

    AppState appReducer(AppState state, action) {  
          return AppState(
            signUpState: signUpReducer(state.signUpState, action),
            loginState: loginReducer(state.loginState, action),
          );
        }
    

    How would I do it then?

    Edit: I changed the code like this now, but still problem persists

    @immutable
    // Define your State
    class AppState {
      final SignUpState signUpState;
      final LoginState loginState;
    
      AppState(this.signUpState, this.loginState);
    }
    
    AppState appReducer(AppState state, action) => new AppState(
      signUpReducer(state.signUpState, action),
      loginReducer(state.loginState, action),
    );
    
    class Redux {
      static Store<AppState> _store;
    
      static Store<AppState> get store {    
        if (_store == null) {
          throw Exception("store is not initialized");
        } else {       
          return _store;
        }
      }
    
      static Future<void> init() async {
        // print(1);
        final signUpStateInitial = SignUpState.initial();
        // print(2);
        final loginStateInitial = LoginState.initial();
        // print(3);
    
        _store = Store<AppState>(
          appReducer,
          middleware: [thunkMiddleware, new LoggingMiddleware.printer()],
          initialState: AppState(
              signUpStateInitial,
              loginStateInitial,
          ),
        );
      }
    }
    

    Edit: this is the signup state (it does not do much since I was only testing)

    @immutable
    class SignUpState {
      final bool isError;
      final bool isLoading;
      final bool isLoggedIn;
    
      SignUpState({
        this.isError,
        this.isLoading,
        this.isLoggedIn,
      });
    
      factory SignUpState.initial() => SignUpState(
        isLoading: false,
        isError: false,
        isLoggedIn: false,
      );
    
      SignUpState copyWith({
        @required bool isError,
        @required bool isLoading,
        @required bool isLoggedIn,
      }) {
        return SignUpState (
          isError: isError ?? this.isError,
          isLoading: isLoading ?? this.isLoading,
          isLoggedIn: isLoggedIn ?? this.isLoading,
        );
      }
    }
    

    And the signup reducer

    signUpReducer(SignUpState prevState, SetSignUpStateAction action) {   
      final payload = action.signUpState;
      print(action); // prints Instance of 'SetSignUpStateAction'
      print(prevState); // prints Instance of 'SignUpState'
      print("signUpReducer");
      return prevState.copyWith(
        isError: payload.isError,
        isLoading: payload.isLoading,
        isLoggedIn: payload.isLoggedIn,
      );
    }
    

    The print statement executes

    Edit 2:

    This is the login reducer, but nothing is going through here yet, I don't have any login actions implemented

    loginReducer(LoginState prevState, SetLoginStateAction action) {
      final payload = action.loginState;
    
      return prevState.copyWith(
        isError: payload.isError,
        isLoading: payload.isLoading,
      );
    }
    
    • Rahul Sharma
      Rahul Sharma over 3 years
      You have this.loginState defined as @required but you are not sending it. I think you might want to solve it.
    • Ravi Singh Lodhi
      Ravi Singh Lodhi over 3 years
      Please show your redux actions also. How are you dispatching & handling them? There might be some issues while managing the actions.
    • user3808307
      user3808307 over 3 years
      @RaviSinghLodhi I added the initial state and reducer for signup, it goes through the reducer, the problem is when coming out
    • user3808307
      user3808307 over 3 years
      I think I am going to start over
    • Ravi Singh Lodhi
      Ravi Singh Lodhi over 3 years
      Please show loginReducer as well. I think you should use dynamic instead of SetSignUpStateAction. Multiple actions will be dispatched from your state. Hence, you should use dynamic. Also, you will have to use if-else conditions to manage the actions.
    • Ravi Singh Lodhi
      Ravi Singh Lodhi over 3 years
      I too had to take multiple snippets from tutorials, blogs, youtube videos etc. Don't worry. I will help you.
    • user3808307
      user3808307 over 3 years
      @RaviSinghLodhi I added it but I think it makes no difference, as I am not dispatching any login actions for now. I will start over today with a different tutorial. If there is one to recommed...
    • Ravi Singh Lodhi
      Ravi Singh Lodhi over 3 years
      When you uncomment your loginReducer & if you dispatch a signup action, it will go to both signUpReducer as well as loginReducer. This will be causing the issue. Just add some checks. I am writing an answer & will update if necessary.