为什么 ref.watch(otherProvider.stream) 在 Riverpod 2.x 中已弃用,并将在 3.x 中删除?

Rik*_*Rik 4 dart flutter riverpod

我正在使用 Riverpod 监听商店中所选商品的更改:

  1. 我有一个PlacesStoreStream<PlaceDetails?> watchPlace(PlaceId placeId)方法的存储库。
  2. 我把它包装在一个家庭提供者中,如下所示:
    @riverpod
    Stream<PlaceDetails?> place(PlaceRef ref, InternalPlaceId placeId) {
        final store = ref.watch(placesStoreProvider);
        return store.watchPlace(placeId);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 我有一个单独的StateProvider, selectedPlaceIdProvider,它使用用户在地图上选择的地点的地点 ID 进行更新。
  4. 我有另一个提供商,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();
     }
    
    Run Code Online (Sandbox Code Playgroud)

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

但是,在使用时ref.watch(placeProvider(placeId).stream)我被警告说它.stream已被弃用。我不太明白我应该用什么来取代它。

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

Rém*_*let 8

.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();
}
Run Code Online (Sandbox Code Playgroud)

在此代码片段中, 的使用.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');
  });
}
Run Code Online (Sandbox Code Playgroud)

然后在这种情况下,您会发现实际上print从未达到,并且selectedPlace从未发出任何内容。
但如果您注释掉该行final place = await container.read(placeProvider(123).future);,那么您的代码就会“工作”。

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

另一方面,拥有.stream并没有给 Riverpod 增加太多价值。您已经可以在没有.stream.

引入弃用的变更日志为您的使用提供了替代语法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);
}
Run Code Online (Sandbox Code Playgroud)

此代码片段的行为就像您期望的 .stream 变体的行为一样。在这种情况下,selectedPlace每当placeProviderselectedPlaceIdProvider更新时都会更新。
但同时,这并不涉及上面讨论的竞争条件问题。之前写main的现在可以按预期工作。