google / flutter_flux

Implementation of the Flux framework for Flutter
https://flutter.io
Apache License 2.0
372 stars 36 forks source link

How to make network calls? #31

Open hvkale opened 6 years ago

hvkale commented 6 years ago

The title of this issue could be a little misleading but I was not sure how else to describe my thought process, so please stay with me and read along.

I am writing a Flutter application that uses flutter_flux. I have a few stores and a few views (widgets) so far. Right now I am in a dilemma on how to do the network API calls. My widget loads but does not have any data to display yet, which is fine, but now it needs to call my network API service that responds with data. Here is what I am thinking design-wise. Please share your opinions on it.

Widget -> Action -> Store -> Network Service Call -> trigger()

In this design, the widget kicks off an action something like fetchMessages and the store listening to this action MessageStore receives a call to this action. In the action callback, this store kicks the network call that returns Future. In the then() callback of that Future, this store mutates it's own data and calls trigger() so that the original view that started this now receives a callback. Similarly, also in catchError of the same Future, this store calls trigger() with some other mutation that maintains error states.

In this design, I am not exactly sure where do I call action from the widget? In build or in initState? My thinking is that in initState the widget will grab data from store but it will be null or empty initially, and build will render empty view but at the same time, kickstarts the cycle described above, which calls back to the widget later and the build function now has data to render, re-grabbed from store.

Do you think it is a good pattern? It still abides to Flux's unidirectional data flow and does not let store data to be manipulated in any way other than an action callback.

hvkale commented 6 years ago

Actually thinking about it more made me realize it is definitely not the best idea to call this action from Widget's build function for various reasons

  1. Flutter calls Widget's build multiple times during its lifetime and it is not ideal to do something as heavy as a network call every time build is called.
  2. A way to overcome point 1 above is to guard it in some condition, but really what could be a better condition in this case? Either maintain flags that tell you if this call was previously made, if yes, do not make another call or check in the state and if data is null or empty, make a call.
  3. Using flags could get really messy really fast and then you have to be extremely careful flipping those at various places.
  4. Checking in state for null or empty data isn't ideal either cause null or empty data could be legitimate states. It does not always mean that network call was never made.

I think the best approach would be that the store (MessageStore in this example) also listens to user auth state changes and as soon as the user is logged in, this store also receives an action callback and kickstarts the network fetch instead of waiting until widget does it.

This approach works well in this case but also leaves a question, what if this MessageStore were to depend on some data from UserStore? Meaning that both UserStore and MessageStore listen to same action callback but for MessageStore to react to that action callback, it needs something from UserStore. In this case how do you make sure that UserStore always finishes its execution before MessageStore starts its execution? I believe React's Flux has API waitFor()