我正在使用Riverpod监听对我的store 中选定商品的更改:

  1. 我有一个存储库PlacesStore,它有一个Stream<PlaceDetails?> watchPlace(PlaceId placeId)方法.
  2. 我把这个包在一个家庭供应商里,就像这样:
    @riverpod
    Stream<PlaceDetails?> place(PlaceRef ref, InternalPlaceId placeId) {
        final store = ref.watch(placesStoreProvider);
        return store.watchPlace(placeId);
    }
    
  3. 我有一个单独的StateProvider,selectedPlaceIdProvider,它使用用户在 map 上 Select 的地点的地点ID进行更新.
  4. 我还有另一个Provider ,selectedPlaceProvider,它将两者结合成这样:
    @riverpod
    Stream<PlaceDetails?> selectedPlace(SelectedPlaceRef ref) {
        final placeId = ref.watch(selectedPlaceIdProvider);
        if (placeId != null) {
            return ref.watch(placeProvider(placeId).stream);
        }
    
        return const Stream.empty();
     }
    

我之所以这样做,是因为我有许多小部件想知道用户 Select 了哪个地方,所以我认为将其放入包含当前所选地点详细信息的提供程序中是有意义的(同时仍然维护家庭提供程序,以便可以查找特别的地点).

然而,当我使用ref.watch(placeProvider(placeId).stream)时,我被警告.stream是不推荐使用的.我不太明白我要用什么来取代它.

上述模式是否有效?如果是这样的话,我应该用什么来替换.stream,这样我仍然可以听到对我当前 Select 的位置的任何更改.

推荐答案

.stream修饰符已经被弃用,因为它几乎总是被糟糕地使用,导致复杂的难以发现的错误.

您在这个问题中给出的片段也面临着同样的问题.您提供的代码片段有一个错误:

@riverpod
Stream<PlaceDetails?> selectedPlace(SelectedPlaceRef ref) {
    final placeId = ref.watch(selectedPlaceIdProvider);
    if (placeId != null) {
        return ref.watch(placeProvider(placeId).stream);
    }

    return const Stream.empty();
}

在这段代码中,.stream的使用引入了一个问题,现在selectedPlace的行为取决于读取提供程序的顺序.

就说你做到了:

void main() async {
  final container = ProviderContainer();
  final place = await container.read(placeProvider(123).future);

  container.read(selectedPlaceIdProvider.notifier).state = 123;
  container.listen(selectedPlaceProvider, (_, value) {
    print('Place $value');
  });
}

Then in this scenario, you'll find that the print is in fact never reached, and selectedPlace never emits anything.
But if you were to comment out the final place = await container.read(placeProvider(123).future); line, then your code would "work".

这实际上意味着使用.stream引入了竞争条件.除非您熟悉这个问题,否则很难发现它,并且可能导致严重的可维护性问题.

另一方面,.stream并不会给Riverpod增加太多价值.没有.stream,你已经可以做任何事情了.

引入弃用的ChangeLog为您的用法提供了替代语法https://github.com/rrousselGit/riverpod/blob/master/packages/riverpod/CHANGELOG.md#230

在您的例子中,您可以重构selectedPlaceProvider以:

@riverpod
Future<PlaceDetails?> selectedPlace(SelectedPlaceRef ref) {
    final placeId = ref.watch(selectedPlaceIdProvider);
    if (placeId == null) return null;

    return ref.watch(placeProvider(placeId).future);
}

This snippet behave like you would expect the .stream variant to behave. In that scenario, selectedPlace will update whenever either placeProvider or selectedPlaceIdProvider update.
But at the same time, this doesn't involve the race-condition problem discussed above. That main wrote previously would now work as expected.

Flutter相关问答推荐

在Riverpod ConsumerWidget中使用文本编辑控制器

build_runner显示错误没有为类ClassDeclaration定义getter宏关键字'

ProviderContainer和GoRouter

Flutter 翼doctor 显示的错误,我似乎不能修复.问题:系统32、Android工具链、Windows 10 SDK

修复后期变量的抖动皮棉

设置Flutter 中的ListView.Builder()的最小高度

将Riverpods NotifierProvider与State类一起使用

如何将请求字段正确添加到DART多部分请求

如何在 Flutter 中创建厚度沿着容器逐渐减小的边框

Flutter中不使用Container是否可以做margin

如何在Flutter 中使用选定的键和值从 map 列表创建 map

GridView 渲染项目之间有微小的间隙

我无法找到修复此错误的位置,指出无法使用静态访问来访问实例成员

如何正确地将 PageView 设置为 AppBar 的标题?

Flutter RIverpod 奇怪地改变状态

如何将 chrome web 模拟器添加到 vs code

Flutter使Perspective Page View无限循环

单元测试中如何比较NaN和NaN是否相等

如何使用 Row 和 Column 制作 L 形布局?

如何从 Firebase Firestore 中获取具有特定字段值的文档?