felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.79k stars 3.39k forks source link

[Question] Why is RepositoryProvider needed? #1007

Closed theDarkBoffin closed 4 years ago

theDarkBoffin commented 4 years ago

Hey, thanks for the promising library. I'm finding it really helpful for state management. I'm using the library for quite a few months. I searched for related existing issues to help answer my question but didn't find any satisfactory answers. The question is, going by the architecture, UI must talk to BLoC, and BLoC will provide necessary data using Repository/Data providers. So, why should a RepositoryProvider be injected in the UI, if the UI doesn't talk to the Repository at all? Couldn't the BLoC instantiate and maintain its Repository?
I just want to find out if I'm missing out on its advantages. Thanks.

felangel commented 4 years ago

Hi @theDarkBoffin 👋 Thanks for opening an issue!

A few reasons why you might not want the bloc to instantiate and maintain it's own Repository are:

Hope that helps!

narcodico commented 4 years ago

The downside is that anyone could easily do context.repository<...>() and directly interact with it from anywhere in the sub-tree 😐 I use get it with injectable and let it inject the dependencies so I don't have to use RepositoryProviders. But at long as people are aware of dos and don'ts it shouldn't matter too much.

theDarkBoffin commented 4 years ago

Hey @felangel, thanks for the quick response! Your response makes good sense. If I may, I still have the following questions:

  1. If Data providers have some common dependency (say http clients), shouldn't they be imported from a separate global file, rather than injecting it down the hierarchy? I understand it helps for unit tests, but if Data Providers can support dependency injection, isn't it enough?
  2. Yes, reusing repositories is certainly important. But, as I said, couldn't it be passed by a parent BLoC instead of RepositoryProvider?
  3. Again, if BLoC supports dependency injection via constructor, isn't it enough to help unit tests?
  4. To me, I find passing down the repositories using BLoC easier than using RepositoryProvider. Is it creating any anti-pattern?

RepositoryProvider needs me to create a new widget to provide the repository to a particular BLoC (As you've said, provider widgets must be different from Presenter widgets). I first need to create a widget for RepositoryProvider, and another for BlocProvider, and then I can typically use the created BLoCs using BlocBuilders. I posed this question because, if the RepositoryProvider is not really needed for establishing good pattern, it would help write lesser boilerplate, and helps not to create anti-patterns where users might directly fetch pieces of data from repository in the UI as @RollyPeres said. No RepositoryProvider means ui has no direct relationship with repository at all.

Again, pardon me if this is sounding lame, because I'm trying to understand.

felangel commented 4 years ago

But, as I said, couldn't it be passed by a parent BLoC instead of RepositoryProvider?

@theDarkBoffin can you please provide a code snippet to show what you mean? Ultimately, where the repository comes from is up to you. You can use get_it or some other DI library aside from RepositoryProvider and they both help achieve the same result 👍

theDarkBoffin commented 4 years ago

@felangel, thanks for the clarification. Here is a code snippet that shows what I meant:

class Parent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<AuthBloc, State>(
      create: (_) => BlocA(auth: AuthRepo()),
      child: Child(),
    );
  }
}

class Child extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<SomeBloc, State>(
      create: (_) => SomeBloc(repo: BlocProvider.of<AuthBloc>(context).auth), //Re-using auth repo by referencing via AuthBloc
      child: Child(),
    );
  }
}

However, I don't think this is a great pattern to re-use repositories either, as it might not suit well for multiple Blocs in the same level of the widget tree. But what I do know is, RepositoryProvider created a cognitive overhead, and more boilerplate code while I was initially using the library. And yes, any DI frameworks can be used to provide repositories. You can give some comments on the above code, if any, or consider this closed. Thanks for your time!

Best regards.

felangel commented 4 years ago

@theDarkBoffin thanks for providing the snippet! I think that approach works well if you can create multiple instances of your repositories. RepositoryProvider is more useful when you need to reuse a single instance throughout your application.

But what I do know is, RepositoryProvider created a cognitive overhead, and more boilerplate code while I was initially using the library.

Thanks for the feedback! I will try to add more in the docs about when to use RepositoryProvider in hopes of clarifying some of these questions.