Skip to content

Commit

Permalink
📃 docs(zh-Hans/combining_requset): tr
Browse files Browse the repository at this point in the history
  • Loading branch information
yang-lile committed Dec 9, 2023
1 parent 0d6f111 commit 9db8edb
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Combining requests
title: 组合请求
version: 1
---

Expand All @@ -12,15 +12,27 @@ import watchPlacement from "./combining_requests/watch_placement";
import listenExample from "./combining_requests/listen_example";
import readExample from './combining_requests/read_example'

<!---
Up till now, we've only seens cases were requests are independent from each
other. But a common use-case is to have to trigger a request based on the
result of another request.
-->
到目前为止,我们只看到请求彼此独立的案例。
但一个常见的用例是必须根据另一个请求的结果触发请求。

<!---
We _could_ be using the <Link documentID="essentials/passing_args" /> mechanism to
do that, by passing the result of a provider as parameter to another a provider.
-->
我们可以使用<Link documentID="essentials/passing_args" />机制来做到这一点,
方法是将一个提供程序的结果作为参数传递给另一个提供程序。

<!---
But this approach has a few downsides:
-->
但这种方法有一些缺点:

<!---
- This leaks implementation details.
Now, your UI needs to know about all the providers that are used
your other provider.
Expand All @@ -30,116 +42,234 @@ But this approach has a few downsides:
- It makes combining requests harder.
- This makes tooling less useful. A devtool wouldn't
know about the relationship between providers.

-->
- 这泄露了实现细节。
现在,UI 需要了解所有被其他提供程序使用的提供程序。
- 每当参数发生变化时,都会产生一个全新的状态。
通过传递参数,当参数更改时,无法保持以前的状态。
- 它使合并请求变得更加困难。
- 这使得开发工具的用处降低。devtool 不会知道提供程序之间的关系。

<!---
To improve this, Riverpod offers a different approach to combine requests.
-->
为了改善这一点,Riverpod 提供了一种不同的方法来合并请求。

<!---
## The basics: Obtaining a "ref"
-->
## 基础知识:获取 "ref"

<!---
All possible ways of combining requests have one thing in common:
They are all based on the `Ref` object.
-->
组合请求的所有可能方法都有一个共同点:它们都基于 `Ref` 对象。

<!---
The `Ref` object is an object which all providers have access to.
It grants them access to various life-cycle listeners, but also
various methods to combine providers.
-->
`Ref` 对象是所有提供程序都有权访问的对象。
它允许他们访问各种生命周期侦听器,
还可以使用各种方法来组合提供程序。

<!---
The place where `Ref` can be obtained depends on the type of provider.
-->
可以获取的位置 `Ref` 取决于提供商的类型。

<!---
In functional providers, the `Ref` is passed as parameter to the
provider's function:
-->
在函数提供程序中,将 `Ref` 作为参数传递给提供程序的函数:

<AutoSnippet {...functionalRef} />

<!---
In class variants, the `Ref` is a property of the Notifier class:
-->
在类变体中,`Ref` 是通知者类的一个属性:

<AutoSnippet {...notifierRef} />

<!---
## Using ref to read a provider.
-->
## 使用 ref 读取提供程序。

## The `ref.watch` method.
<!---
### The `ref.watch` method.
-->
### `ref.watch` 方法。

<!---
Now that we've obtained a `Ref`, we can use it to combine requests.
The main way to do so is by using `ref.watch`.
It is generally recommended to architecture your code such that you
can use `ref.watch` over other options, as it is generally easier to maintain.
-->
现在我们已经获得了一个 `Ref`,我们可以用它来组合请求。
执行此操作的主要方法是使用 `ref.watch`。
通常建议对代码进行架构设计,
以便可以使用 `ref.watch` 的其他选项,因为它通常更易于维护。

<!---
The `ref.watch` method takes a provider, and returns its current state.
Then, whenever the listened provider changes, our provider will be
invalidated and rebuilt next frame or on next read.
-->
`ref.watch` 方法采用提供程序,并返回其当前状态。
然后,每当侦听的提供程序发生更改时,我们的提供程序将在
下一帧或下一次读取时失效并重新生成。

<!---
By using `ref.watch`, your logic becomes both "reactive" and "declarative".
Meaning that your logic will automatically recompute when needed.
And that the update mechanism doesn't rely on side-effects, such as an "on change".
This is similar to how StatelessWidgets behave.
-->
通过使用 `ref.watch`,您的逻辑变得既是“反应式”又是“声明式”。
这意味着您的逻辑将在需要时自动重新计算。
并且更新机制不依赖于副作用,例如“更改”。
这类似于 StatelessWidgets 的行为方式。

<!---
As an example, we could define a provider that listens to the user's location.
Then, we could use this location to fetch the list of restaurants near the user.
-->
例如,我们可以定义一个监听用户位置的提供程序。
然后,我们可以使用这个位置来获取用户附近的餐馆列表。

<AutoSnippet {...watchExample} />

:::info
<!---
When the listened provider changes and our request recomputes, the previous
state is kept until the new request completes.
At the same time, while the request is pending, the "isLoading" and "isReloading"
flags will be set.
-->
当侦听的提供程序发生更改并且我们的请求重新计算时,将保留以前的状态,直到新请求完成。
同时,当请求处于挂起状态时,将设置 "isLoading" "isReloading" 标志。

<!---
This enables UI to either show the previous state, or a loading indicator,
or even both.
-->
这使 UI 能够显示以前的状态或加载指示器,甚至两者兼而有之。
:::

:::info
<!---
Notice how we used `ref.watch(locationProvider.future)` instead of `ref.watch(locationProvider)`.
That is because our `locationProvider` is asynchronous. As such, we want to
await for an initial value to be available.
-->
请注意我们如何使用 `ref.watch(locationProvider.future)` 来替代 `ref.watch(locationProvider)`。
那是因为我们 `locationProvider` 是异步的。因此,我们希望等待初始值可用。

<!---
If we omit that `.future`, we would receive an `AsyncValue`, which is a snapshot
of the current state of the `locationProvider`. But if no location is available yet,
we wouldn't be able to do anything.
-->
如果我们省略了这一点 `.future`,我们将收到一个 `AsyncValue`,
它是 `locationProvider` 当前状态的快照。但是,如果还没有可用的位置,
我们将无能为力。
:::

:::caution
<!---
It is considered bad practice to call `ref.watch` inside code that is executed
"imperatively". Meaning any code that is possibly not executed during the build
phase of the provider. This includes "listener" callbacks or methods on Notifiers:
-->
调用 `ref.watch` “命令式”执行的内部代码被认为是不好的做法。
这意味着在提供程序的构建阶段可能未执行的任何代码。
这包括通告程序上的“侦听器”回调或方法:

<AutoSnippet {...watchPlacement} />
:::

<!---
## The `ref.listen`/`listenSelf` methods.
-->
### `ref.listen`/`listenSelf` 方法。

<!---
The `ref.listen` method is an alternative to `ref.watch`.
It is similar to your traditional "listen"/"addListener" method. It takes a provider
and a callback, and will invoke said callback whenever the content of the provider
changes.
-->
`ref.listen` 方法是 `ref.watch` 的替代方法。
它类似于传统的 "listen"/"addListener" 方法。
它接受一个提供程序和一个回调,
并在提供程序的内容发生更改时调用该回调。

<!---
Refactoring your code such that you can use `ref.watch` instead of `ref.listen`
is generally recommended, as the latter is more error-prone due to its imperative nature.
But `ref.listen` can be helpful to add some quick logic without having to do
significant refactor.
-->
通常建议重构代码,您可以使用 `ref.watch` 替代 `ref.listen`,
因为后者由于其命令性质而更容易出错。
但是 `ref.listen` 可以有助于添加一些快速逻辑而不必进行重大重构。

<!---
We could rewrite the `ref.watch` example to use `ref.listen` instead
-->
我们可以重写 `ref.watch` 示例并使用 `ref.listen` 代替

<AutoSnippet {...listenExample} />

:::info
<!---
It is entirely safe to use `ref.listen` during the build phase of a provider.
If the provider somehow is recomputed, previous listeners will be removed.
-->
在提供程序的构建阶段使用 `ref.listen` 是完全安全的。
如果以某种方式重新计算提供程序,则以前的侦听器将被删除。

<!---
Alternatively, you can use the return value of `ref.listen` to remove the listener
manually when you wish.
-->
或者,您可以根据需要使用 `ref.listen` 的返回值手动删除侦听器。
:::

<!---
## The `ref.read` method.
-->
### `ref.read` 方法

<!---
The last option available is `ref.read`.
It is similar to `ref.watch` in that it returns the current state of a provider.
But unlike `ref.watch`, it doesn't listen to the provider.
-->
最后一个可用选项是 `ref.read`。
它类似于 `ref.watch` 返回提供程序的当前状态。
但与 `ref.watch` 不同的是,它不监听提供者。

<!---
As such, `ref.read` should be only be used in placed where you can't use
`ref.watch`, such as inside methods of Notifiers.
-->
因此,`ref.read` 应该只被用在你不能使用 `ref.watch` 的地方,
比如通告程序的内部方法。

<AutoSnippet {...readExample} />

:::caution
<!---
Be careful when using `ref.read` on a provider as, since it doesn't listen to the
provider, said provider may decide to destroy its state if it isn't listened.
-->
`ref.read` 在提供程序上使用时要小心,因为它不侦听提供程序,
因此如果不侦听提供程序,则该提供程序可能会决定销毁其状态。
:::
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int other(OtherRef ref) => 0;
/* SNIPPET START */
@riverpod
int example(ExampleRef ref) {
// "Ref" can be used here to read other providers
// "Ref" 可以在这里用来阅读其他提供商
final otherValue = ref.watch(otherProvider);

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ final otherProvider = Provider<int>((ref) => 0);

/* SNIPPET START */
final provider = Provider<int>((ref) {
// "Ref" can be used here to read other providers
// "Ref" 可以在这里用来阅读其他提供商
final otherValue = ref.watch(otherProvider);

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ int other(OtherRef ref) => 0;
class Example extends _$Example {
@override
int build() {
// "Ref" can be used here to read other providers
// "Ref" 可以在这里用来阅读其他提供商
final otherValue = ref.watch(otherProvider);

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ final provider = NotifierProvider<MyNotifier, int>(MyNotifier.new);
class MyNotifier extends Notifier<int> {
@override
int build() {
// "Ref" can be used here to read other providers
// "Ref" 可以在这里用来阅读其他提供商
final otherValue = ref.watch(otherProvider);

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ final otherProvider = Provider<int>((ref) => 0);
class MyNotifier extends _$MyNotifier {
@override
int build() {
// Bad! Do not use "read" here as it is not reactive
// 糟糕的!不要在这里使用 "read",因为它不是反应性的
ref.read(otherProvider);

return 0;
}

void increment() {
ref.read(otherProvider); // Using "read" here is fine
ref.read(otherProvider); // 这里使用 "read" 就可以了
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ final notifierProvider = NotifierProvider<MyNotifier, int>(MyNotifier.new);
class MyNotifier extends Notifier<int> {
@override
int build() {
// Bad! Do not use "read" here as it is not reactive
// 糟糕的!不要在这里使用 "read",因为它不是反应性的
ref.read(otherProvider);

return 0;
}

void increment() {
ref.read(otherProvider); // Using "read" here is fine
ref.read(otherProvider); // 这里使用 "read" 就可以了
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ const someStream = Stream<({double longitude, double latitude})>.empty();
/* SNIPPET START */
@riverpod
Stream<({double longitude, double latitude})> location(LocationRef ref) {
// TO-DO: Return a stream which obtains the current location
// TO-DO: 返回获取当前位置的流
return someStream;
}

@riverpod
Future<List<String>> restaurantsNearMe(RestaurantsNearMeRef ref) async {
// We use "ref.watch" to obtain the latest location.
// By specifying that ".future" after the provider, our code will wait
// for at least one location to be available.
// 我们使用 "ref.watch" 来获取最新位置。
// 通过在提供程序之后指定 ".future"
// 我们的代码将在等待到至少一个位置信息后可用。
final location = await ref.watch(locationProvider.future);

// We can now make a network request based on that location.
// For example, we could use the Google Map API:
// 我们现在可以根据该位置发出网络请求。
// 例如,我们可以使用 Google Map API
// https://developers.google.com/maps/documentation/places/web-service/search-nearby
final response = await http.get(
Uri.https('maps.googleapis.com', 'maps/api/place/nearbysearch/json', {
Expand All @@ -36,7 +36,7 @@ Future<List<String>> restaurantsNearMe(RestaurantsNearMeRef ref) async {
'key': '<your api key>',
}),
);
// Obtain the restaurant names from the JSON
// JSON 中获取餐厅名称
final json = jsonDecode(response.body) as Map;
final results = (json['results'] as List).cast<Map<Object?, Object?>>();
return results.map((e) => e['name']! as String).toList();
Expand Down
Loading

0 comments on commit 9db8edb

Please sign in to comment.