mcpField
R
riverpod-faq-and-practices

riverpod-faq-and-practices

github.com/serverpod开发1084 次安装34 周前更新
代码仓库

关于 riverpod-faq-and-practices

面向 Flutter 开发者的 Riverpod 参考技能,汇总 Provider 生命周期、Widget 集成、状态放置和常见问题的实践建议。

主要功能

  1. 01说明 ref.refresh 与 ref.invalidate
  2. 02介绍 Ref 和 WidgetRef 的用法
  3. 03Provider 初始化实践
  4. 04区分本地状态与共享状态
  5. 05riverpod_lint 使用建议

适用场景

  1. 01选择用户操作后的刷新方式
  2. 02避免 await 后不安全地使用 ref
  3. 03判断状态是否应放入 Provider
  4. 04检查 Provider 定义中的常见问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serverpod/skills-registry --skill riverpod-faq-and-practices

安装后在 Claude Code 中输入/riverpod-faq-and-practices确认已加载。

详细介绍

Riverpod — 常见问题与最佳实践

常见问题

ref.refresh 与 ref.invalidate

ref.refresh = ref.invalidate + ref.read:它会使 provider 失效并返回新值。如果不需要新值(例如“用户下拉刷新时重新计算”),请使用 invalidate。如果需要立即获得结果,请使用 refresh。仅调用 invalidate 后,可能会在下一帧或下次读取该 provider 时重新计算;refresh 会通过读取强制立即重新计算。

为什么 Ref 和 WidgetRef 没有共用接口?

Ref(用于 providers)和 WidgetRef(用于 widgets)特意保持分离,这样你就不会编写有条件地依赖两者的代码;它们存在细微差异,混用容易出错。优先使用 Ref:将逻辑放入 Notifier,并在 UI 中调用 ref.read(notifierProvider.notifier).yourMethod();该方法会使用 Notifier 的 Ref。

为什么使用 ConsumerWidget,而不是 StatelessWidget?

InheritedWidget(因此也包括 BuildContext)无法支持像 ref.listen 这样的“变更时”监听器,无法获知 widgets 何时停止监听(auto-dispose 和 family 需要此功能),而且存在生命周期方面的怪异行为(例如使用 GlobalKeys 时)。Riverpod 需要一个不依赖 BuildContext 的 Ref 来实现这些功能。因此,ConsumerWidget(以及 Consumer)会提供一个 Ref。

为什么 hooks_riverpod 不导出 flutter_hooks?

这样每个包就可以独立进行版本管理;一个包中的破坏性变更不会迫使另一个包也进行破坏性变更。

一次重置所有 providers?

没有一次重置所有 providers 的 API;这被视为反模式。对于“登出并清除状态”,让依赖当前用户的 providers 通过 ref.watch 监听用户 provider;用户登出时,该 provider 会发生变化,其依赖项便会重新计算或清除。只有依赖用户的状态会重置;其余状态会保留。

“使用 ref 时,widget 即将卸载或已经卸载是不安全的”

当 widget 可能已经被释放后仍使用 ref(例如在 await 之后)时,就会出现此问题(或提示“No ProviderScope found”)。每次 await 之后,在使用 ref 前检查 if (!context.mounted) return;(与使用 BuildContext 时的做法相同)。


应做 / 不应做

避免在 widget 中初始化 providers

providers 应自行初始化。不要在 initState 中调用类似 ref.read(provider).init() 的代码。如果初始化取决于用户操作(例如导航),请在该操作中触发初始化(例如在 Navigator.push 前调用 onPressed)。

避免将 providers 用于临时状态

providers 应用于共享业务状态,而不应用于:选中的标签页/项目、表单状态(离开或返回时应重置)、动画或类似控制器的状态(例如 TextEditingController)。对于本地 widget 状态,请使用 flutter_hooks 或本地状态。将“选中的项目”存储在全局 provider 中可能会破坏返回导航(例如,从 /books/21 返回时应显示 /books/42,但 provider 中仍保存着 21)。

不要在 provider 初始化期间执行副作用

providers 应表示“读取”操作。不要在 provider 的 build 中执行“写入”操作(例如提交表单)。这可能导致副作用被跳过或重复执行。对于副作用的加载/错误状态,请使用 mutations(参见 riverpod-mutations)。

优先使用静态已知的 providers 与 ref.watch/read/listen

使用 ref.watch(provider),其中 provider 是顶层 final 变量(或以其他方式静态可知)。避免将 provider 作为参数传入后再监听,这样静态分析和 riverpod_lint 才能正常工作。

避免动态创建 providers

将 providers 定义为顶层 final 变量。不要在类内部或作为实例字段创建它们;这可能导致内存泄漏和不受支持的行为。类中的 static final 可以使用,但代码生成不支持这种方式。

启用 riverpod_lint(参见 riverpod-getting-started),以强制执行其中许多实践。

评论

支持 Markdown