marcglasberg / async_redux

Flutter Package: A Redux version tailored for Flutter, which is easy to learn, to use, to test, and has no boilerplate. Allows for both sync and async reducers.
Other
230 stars 41 forks source link

Handling local state for screen, need suggestions. #157

Closed foxy17 closed 4 months ago

foxy17 commented 4 months ago

Hey @marcglasberg loving the package and have used it extensively for my app. The only dilemma I am facing is whether I should use this for state that is local to my widgets/screens. Right now I have a global app state that I use to store things that are needed across screens like the user profile details and auth details.

But lets say now I have a screen which has multiple states like loader , a call to get user location, call to update user location. Right now I am using a stateless widget to do this which results in a big file more than 500 lines and my code being coupled with UI. At the same time I don't want to couple this with the global state, since I want to get rid of it when the screen offloads and loads.

What do you suggest in this case? Should I create local stores for the screens?

kuhnroyal commented 4 months ago

I am using sub states for UI that do not get persisted in combination with StoreConnector.onInit/dispose to prepare and cleanup the state.

marcglasberg commented 4 months ago

@foxy17 Glad you like it. Please visit asyncredux.com and read the docs. It will give you good ideas.

Your app must have a single store. @kuhnroyal is right, you should create substates, not additional "local" stores.

Note Async Redux state is not really only "global" state. It's any state you need to make your app easy to use.

For example, if your online store has a CartWidget that's only used when you are inside an "online store screen", you can have a Cart state:

class State {
   final Cart cart;
   ...
}

Or you can have an online store containing a cart:

class State {
   final OnlineStore onlineStore;
   ...
}

class OnlineStore {
   final Cart cart;
   ...
}

You can initiate the cart when you enter the OnlineStoreConnector in its StoreConnector.onInit if you use the StoreConnector, or in OnlineStoreScreen.initState if you don't use the store connector but your OnlineStoreScreen is a stateful widget.

You don't necessarily need to delete the state when you leave the OnlineStoreScreen, you can just abandon it. But if you want you can, simply by dispatching a CleanCartAction in StoreConnector.onDispose or in OnlineStoreScreen.dispose:

class CleanCartAction extends AppAction {
  State reducer() => state.copy(state.cart: Cart.empty);
}

You should persist the Cart substate to the local device (using the persistor) only if you want the cart contents to still be there when the user closes the app and comes back later. It's not really related to the state being global or local.

I think the rule whether to put state in Async Redux's state or not is that you should do it if it makes things easier for you. For example, widgets with controllers like TextField or ListView should have their local controllers created in the stateful widgets that contain them, because that's the easiest thing to do. In this case, AsyncRedux can interact with this controllers through Events: https://asyncredux.com/flutter/miscellaneous/events

foxy17 commented 4 months ago

This makes sense and I thought about doing this as the first solution,but this results in my app state class becoming super big since I have around 20 screens that I want to decouple my logic from. Is that okay now that it results in 50+ things in my app state?

marcglasberg commented 4 months ago

There is no problem your state being big, as long as it's easy to understand and you don't get lost in it. Think about clean-code, not size. Name things correctly (very important!), and nest substates where it makes sense (for example, adding Cart inside OnlineStore instead of putting Cart in the root state).