diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args.mdx index aa0146c63..f1e3673d8 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args.mdx @@ -1,5 +1,5 @@ --- -title: Passing arguments to your requests +title: 将参数传递给您的请求 version: 1 --- @@ -15,33 +15,58 @@ import multipleConsumerFamily from "!!raw-loader!./passing_args/raw/multiple_con import tupleFamily from "!!raw-loader!./passing_args/raw/tuple_family.dart"; import consumerTupleFamily from "!!raw-loader!./passing_args/raw/consumer_tuple_family.dart"; + +在上一篇文章中,我们看到了如何定义一个“provider”来发出一个简单的 _GET_ HTTP 请求。 +但通常,HTTP 请求依赖于外部参数。 + +例如,以前我们使用 [Bored API](https://boredapi.com/) 向用户推荐随机活动。 +但也许用户想要过滤他们想做的活动类型,或者有价格要求,等等…… +这些参数事先是未知的。因此,我们需要一种方法将这些参数 +从我们的 UI 传递到我们的提供程序。 + +## 更新我们的提供者程序以接受参数 + +提醒一下,以前我们是这样定义我们的提供程序的: + +当不依赖于代码生成时,我们需要稍微调整定义提供程序的语法,以支持传递参数。 +这是通过依靠称为“family”的“修饰符”来完成的。 + +简而言之,我们需要在提供程序的类型之后添加 `.family` 一个额外的类型参数, +以及与参数类型相对应的类型参数。 +例如,我们可以更新提供程序以接受与所需活动类型相对应的 String 参数: @@ -49,110 +74,191 @@ corresponding to the type of activity desired: + +要将参数传递给我们的提供程序,我们只需在带注解的函数本身上添加参数即可。 +例如,我们可以更新提供程序以接受与所需活动类型相对应的 `String` 参数: :::caution + +将参数传递给提供程序时,强烈建议在提供程序上启用“autoDispose”。 +否则可能会导致内存泄漏。 +有关详细信息,请参阅。 ::: + +## 更新我们的 UI 以传递参数 + +以前,小部件是这样消费我们的提供程序的: + +但是现在我们的提供程序收到参数,使用它的语法略有不同。 +提供程序现在是一个函数,需要使用请求的参数来调用它。 +我们可以更新我们的 UI 以传递硬编码类型的活动,如下所示: + +传递给提供程序的参数对应于带注解的函数的参数,减去“ref”参数。 :::info + +完全可以同时侦听具有不同参数的同一提供程序。 +例如,我们的 UI 可以同时呈现“娱乐(recreational)”_和_“烹饪(cooking)”活动: + +这就是修饰符被称为“family”的原因: +因为把参数传递给提供程序(译者注:作为不同状态的区分的 key 值), +可以有效地将提供程序转换为一组具有相同逻辑的状态。 ::: + +## 缓存注意事项和参数限制 + +将参数传递给提供程序时,仍会缓存计算。 +不同之处在于,计算现在是按参数缓存的。 + +这意味着,如果两个小部件使用具有相同参数的同一提供程序,则只会发出单个网络请求。 +但是,如果两个小部件使用具有不同参数的同一提供程序,则将发出两个网络请求。 + +为此,Riverpod 依赖于参数的 `==` 运算符。 +因此,传递给提供程序的参数必须具有一致的相等性,这一点很重要。 :::caution + +一个常见的错误是直接实例化一个新对象作为提供程序的参数,但该对象没有重写 `==`。 +例如,您可能很想去这样使用 `List`: + +此代码的问题在于 `['recreational', 'cooking'] == ['recreational', 'cooking']` 的结果是 `false`。 +因此,Riverpod 将认为这两个参数不同,并尝试发出新的网络请求。 +这将导致网络请求的无限循环,一直向用户显示进度指示器。 + +要解决此问题,您可以使用 `const` 列表 (`const ['recreational', 'cooking']`) +或使用重写了 `==` 的自定义列表实现。 + +为了帮助发现此错误,建议使用 [riverpod_lint](https://pub.dev/packages/riverpod_lint) +并启用 [provider_parameters](https://github.com/rrousselGit/riverpod/tree/master/packages/riverpod_lint#provider_parameters) +的 lint 规则。这样做之后,上面的代码片段将显示警告。 +有关安装步骤,请参阅。 ::: + +考虑到这一点,您可能想知道如何将多个参数传递给提供程序。 +建议的解决方案是: + +- 切换到代码生成,这样可以传递任意数量的参数 +- 使用 Dart 3 的记录 (record) 语法 + +Dart 3 的记录之所以派上用场,是因为它们自然覆盖 `==` 并具有方便的语法。 +例如,我们可以更新我们的提供商,以同时接受一种活动类型和最高价格: + +然后,我们可以像这样消费这个提供程序: diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/family.dart index 12168d573..d9e7b0ff2 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/family.dart @@ -9,18 +9,18 @@ part 'family.g.dart'; @riverpod Future activity( ActivityRef ref, - // We can add arguments to the provider. - // The type of the parameter can be whatever you wish. + // 我们可以向提供者添加参数。 + // 参数的类型可以是您想要的任何类型。 String activityType, ) async { - // We can use the "activityType" argument to build the URL. - // This will point to "https://boredapi.com/api/activity?type=" + // 我们可以使用“activityType”参数来构建 URL。 + // 这将指向 "https://boredapi.com/api/activity?type=" final response = await http.get( Uri( scheme: 'https', host: 'boredapi.com', path: '/api/activity', - // No need to manually encode the query parameters, the "Uri" class does it for us. + // 无需手动编码查询参数,“Uri”类为我们完成了这一工作。 queryParameters: {'type': activityType}, ), ); diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/provider.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/provider.dart index 2c25b5680..71c45fd98 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/provider.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/codegen/provider.dart @@ -9,24 +9,24 @@ part 'provider.g.dart'; FutureOr fetchActivity() => throw UnimplementedError(); /* SNIPPET START */ -// A "functional" provider +// “函数型”提供商 @riverpod Future activity(ActivityRef ref) async { - // TODO: perform a network request to fetch an activity + // TODO: 执行网络请求以获取活动 return fetchActivity(); } -// Or alternatively, a "notifier" +// 或者替代方案,“通知者” @riverpod class ActivityNotifier2 extends _$ActivityNotifier2 { - /// Notifier arguments are specified on the build method. - /// There can be as many as you want, have any name, and even be optional/named. + /// 通知程序参数在构建方法上指定。 + /// 可以有任意数量的通知程序参数,可以是任意的变量名称,甚至可以是可选/命名的参数。 @override Future build(String activityType) async { - // Arguments are also available with "this." + // 参数也可通过 "this." 使用 print(this.activityType); - // TODO: perform a network request to fetch an activity + // TODO: 执行网络请求以获取活动 return fetchActivity(); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_family.dart index 20077825d..4ad03dbcc 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_family.dart @@ -13,8 +13,8 @@ class Example extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { /* SNIPPET START */ AsyncValue activity = ref.watch( - // The provider is now a function expecting the activity type. - // Let's pass a constant string for now, for the sake of simplicity. + // 提供者现在是一个需要活动类型的函数。 + // 为了简单起见,我们现在传递一个常量字符串。 activityProvider('recreational'), ); /* SNIPPET END */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_list_family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_list_family.dart index ef70e4846..64433d625 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_list_family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_list_family.dart @@ -13,8 +13,8 @@ class Example extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { /* SNIPPET START */ - // We could update activityProvider to accept a list of strings instead. - // Then be tempted to create that list directly in the watch call. + // 我们可以更新 ActivityProvider 接受字符串列表以替换之前的代码。 + // 然后尝试直接在 watch 调用中创建该列表。 ref.watch(activityProvider(['recreational', 'cooking'])); /* SNIPPET END */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_tuple_family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_tuple_family.dart index e4b5947ed..b63149bde 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_tuple_family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/consumer_tuple_family.dart @@ -12,9 +12,8 @@ class Example extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { /* SNIPPET START */ ref.watch( - // Using a Record, we can pass our parameters. - // It is fine to create the record directly - // in the watch call as records override ==. + // 使用记录,我们可以传递参数。 + // 在 watch 调用中,可以实现直接创建带有覆盖 == 功能的记录。 activityProvider((type: 'recreational', maxPrice: 40)), ); /* SNIPPET END */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart index dece07b5b..f0ee5d47b 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart @@ -8,35 +8,35 @@ FutureOr fetchActivity(String activityType) => throw UnimplementedError(); /* SNIPPET START */ -// A "functional" provider +// “函数型”提供者 final activityProvider = FutureProvider.autoDispose - // We use the ".family" modifier. - // The "String" generic type corresponds to the argument type. - // Our provider now receives an extra argument on top of "ref": the activity type. + // 我们使用 ".family" 修饰符。 + // 泛型类型 "String" 对应于参数的类型。 + // 我们的提供程序现在在 "ref" 上收到一个额外的参数:activity 的类型。 .family((ref, activityType) async { - // TODO: perform a network request to fetch an activity using "activityType" + // TODO: 使用 "activityType" 执行网络请求以获取活动 return fetchActivity(activityType); }); -// A "notifier" provider +// “通知者”的提供者 final activityProvider2 = AsyncNotifierProvider.autoDispose - // Again, we use the ".family" modifier, and specify the argument as type "String". + // 再次,我们使用 ".family" 修饰符,并将参数指定为 "String" 类型。 .family( ActivityNotifier.new, ); -// When using ".family" with notifiers, we need to change the notifier subclass: +// 当将 ".family" 与通知程序一起使用时,我们需要更改通知程序子类: // AsyncNotifier -> FamilyAsyncNotifier // AutoDisposeAsyncNotifier -> AutoDisposeFamilyAsyncNotifier class ActivityNotifier extends AutoDisposeFamilyAsyncNotifier { - /// Family arguments are passed to the build method and accessible with this.arg + /// Family 参数传递给构建方法并可通过 this.arg 访问 @override Future build(String activityType) async { - // Arguments are also available with "this.arg" + // 参数也可通过 "this.arg" 使用 print(this.arg); - // TODO: perform a network request to fetch an activity + // TODO: 执行网络请求以获取活动 return fetchActivity(activityType); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/multiple_consumer_family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/multiple_consumer_family.dart index 7cf928adb..68c9620db 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/multiple_consumer_family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/multiple_consumer_family.dart @@ -22,8 +22,8 @@ class Example extends ConsumerWidget { final recreational = ref.watch(activityProvider('recreational')); final cooking = ref.watch(activityProvider('cooking')); - // We can then render both activities. - // Both requests will happen in parallel and correctly be cached. + // 然后我们可以同时渲染这两个活动。 + // 两个请求将并行发生并正确缓存。 return Column( children: [ Text(recreational.valueOrNull?.activity ?? ''), diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/provider.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/provider.dart index 254a71280..cdf67f173 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/provider.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/provider.dart @@ -6,13 +6,13 @@ import '../../first_request/raw/activity.dart'; FutureOr fetchActivity() => throw UnimplementedError(); -// A "functional" provider +// “函数型”提供商 final activityProvider = FutureProvider.autoDispose((ref) async { - // TODO: perform a network request to fetch an activity + // TODO: 执行网络请求以获取活动 return fetchActivity(); }); -// Or alternatively, a "notifier" +// 或者替代方案,“通知者” final activityProvider2 = AsyncNotifierProvider( ActivityNotifier.new, ); @@ -20,7 +20,7 @@ final activityProvider2 = AsyncNotifierProvider( class ActivityNotifier extends AsyncNotifier { @override Future build() async { - // TODO: perform a network request to fetch an activity + // TODO: 执行网络请求以获取活动 return fetchActivity(); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/tuple_family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/tuple_family.dart index c39625d57..43840a881 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/tuple_family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/tuple_family.dart @@ -8,13 +8,12 @@ import 'package:http/http.dart' as http; import '../../first_request/raw/activity.dart'; /* SNIPPET START */ - -// We define a record representing the parameters we want to pass to the provider. -// Making a typedef is optional but can make the code more readable. +// 我们定义一条记录,表示我们想要传递给提供者的参数。 +// 创建 typedef 是可选的,但可以使代码更具可读性。 typedef ActivityParameters = ({String type, int maxPrice}); final activityProvider = FutureProvider.autoDispose - // We now use the newly defined record as the argument type. + // 我们现在使用新定义的记录作为参数类型。 .family((ref, arguments) async { final response = await http.get( Uri( @@ -22,7 +21,7 @@ final activityProvider = FutureProvider.autoDispose host: 'boredapi.com', path: '/api/activity', queryParameters: { - // Lastly, we can use the arguments to update our query parameters. + // 最后,我们可以使用参数来更新请求的查询参数。 'type': arguments.type, 'price': arguments.maxPrice, }, diff --git a/website/src/documents_meta.js b/website/src/documents_meta.js index 526516d78..3a1e3a739 100644 --- a/website/src/documents_meta.js +++ b/website/src/documents_meta.js @@ -65,7 +65,7 @@ export const documentTitles = { "essentials/testing": "Testing your providers", "essentials/side_effects": "执行副作用", "essentials/provider_observer": "Logging and error reporting", - "essentials/passing_args": "Passing arguments to your requests", + "essentials/passing_args": "将参数传递给您的请求", "essentials/first_request": "开始你的第一次 provider/network 请求", "essentials/faq": "FAQ", "essentials/eager_initialization": "Eager initialization of providers",