2

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,
  );
}
user3808307
  • 2,270
  • 9
  • 45
  • 99
  • 1
    You have `this.loginState` defined as `@required` but you are not sending it. I think you might want to solve it. – Rahul Nov 17 '20 at 06:51
  • Please show your redux actions also. How are you dispatching & handling them? There might be some issues while managing the actions. – Ravi Singh Lodhi Nov 17 '20 at 17:48
  • @RaviSinghLodhi I added the initial state and reducer for signup, it goes through the reducer, the problem is when coming out – user3808307 Nov 18 '20 at 14:31
  • I think I am going to start over – user3808307 Nov 18 '20 at 14:38
  • 1
    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 Nov 20 '20 at 03:51
  • 1
    I too had to take multiple snippets from tutorials, blogs, youtube videos etc. Don't worry. I will help you. – Ravi Singh Lodhi Nov 20 '20 at 03:52
  • @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... – user3808307 Nov 20 '20 at 17:42
  • 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. – Ravi Singh Lodhi Nov 21 '20 at 04:31

1 Answers1

4

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,
    );
}
Ravi Singh Lodhi
  • 2,605
  • 1
  • 9
  • 12