Flutter: 小部件挂钩

创建于 2018-12-12  ·  100评论  ·  资料来源: flutter/flutter

React 团队最近公布了 hooks: https: //medium.com/@dan_abramov/making -sense-of-react-hooks-fdbde8803889。 由于 Flutter 与 React 非常相似,看看这些是否也适合 Flutter 可能会很有趣。

定义:

钩子与StatefulWidgetState非常相似,但有一个主要区别:我们可以在一个小部件上拥有任意数量的钩子。
Hooks 可以访问State拥有的所有生命周期(或修改版本)。

钩子可以在任何给定的小部件上使用。 与State相反,它只能用于一种特定的小部件类型。

Hooks 与 super mixins 不同,因为它们不会产生冲突。 挂钩是_完全_独立的,与小部件无关。
这意味着 Hook 可用于存储值并公开公开它,而不必担心冲突。 这也意味着我们可以多次重用同一个 Hook,这与 mixins 不同。

原则:

钩子基本上存储在数组中的Element中。 它们只能从小部件的build方法中访问。 并且应该无条件地访问挂钩,例如:

做:

Widget build(BuildContext context) {
  Hook.use(MyHook());
}

别:

Widget build(BuildContext context) {
  if (condition) {
    Hook.use(MyHook());
  }
}

这个限制可能看起来非常有限,但这是因为钩子是由它们的索引存储的。 不是他们的类型或名称。
这允许根据需要多次重复使用相同的钩子,而不会发生任何冲突。

用例

Hooks 最有用的部分是它们允许将生命周期逻辑提取到可重用的组件中。

Flutter 小部件的一个典型问题是一次性对象,例如AnimationController
它们通常需要initStatedispose覆盖。 但同时出于可维护性的原因不能提取到 mixin 中。

这导致了一个常见的代码片段: stanim

class Example extends StatefulWidget {
  <strong i="7">@override</strong>
  ExampleState createState() => ExampleState();
}

class ExampleState extends State<Example>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  <strong i="8">@override</strong>
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
  }

  <strong i="9">@override</strong>
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  <strong i="10">@override</strong>
  Widget build(BuildContext context) {
    return Container(

    );
  }
}

Hooks 通过提取生命周期逻辑解决了这个问题。 这会导致潜在的 _much_ 更小的代码:

class Example extends StatelessWidget {
  <strong i="15">@override</strong>
  Widget build(BuildContext context) {
    AnimationController controller = useAnimationController(duration: const Duration(seconds: 1));
    return Container(

    );
  }
}

这种钩子的简单实现可能是以下函数:

AnimationController useAnimationController({Duration duration}) {
  // use another hook to obtain a TickerProvider
  final tickerProvider = useTickerProvider();

  // create an AnimationController once
  final animationController = useMemoized<AnimationController>(
    () => AnimationController(vsync: tickerProvider, duration: duration)
  );
  // register `dispose` method to be closed on widget removal
  useEffect(() => animationController.dispose, [animationController]), 

   // synchronize the arguments
  useValueChanged(duration, (_, __) {
    animationController.duration = duration;
  });

  return animationController;
}

useAnimationController是那些“钩子”之一。

这种钩子的幼稚实现如下:

该代码应该看起来类似于使用State类的人。 但这有几个有趣的点:

  • 钩子完全照顾AnimationController ,从创建到处置。 但也有更新。
  • 它可以很容易地提取到可重复使用的包中
  • 它还负责在热重载时更新duration ,而不是在 initState 中创建AnimationController initState
  • 钩子可以使用其他钩子来构建更复杂的逻辑

缺点

Hooks 是有代价的。 对值的访问具有类似于InheritedElement的开销。 而且它们还需要创建一组新的短期对象,例如闭包或“Widget”之类。

我还没有运行一个基准来看到真正的区别


另一个问题是关于热重载。

如果可以的话,在列表末尾添加钩子。 但是由于钩子是根据它们的顺序工作的,所以在现有钩子中间添加钩子会导致部分状态重置。 例子:

A, BA, C, B将重置B的状态(再次调用disposeinitHook )。

结论

Hooks 通过允许更大的代码重用来简化小部件世界。 尤其是在非常常见的场景上,例如 dispose、memoize 和 watch a value。

它们可用于完全替换 StatefulWidget。

但是它们需要思想转变,并且重构时的部分状态重置可能很麻烦。

可以通过创建自定义元素在 Flutter 之外提取钩子。 到目前为止,无需修改源。

但是由于钩子的特殊性,它会从自定义 linter 中受益很多。 目前无法提供哪些外部包。

奖金

~这里有一个正在进行的实现: https ://github.com/rrousselGit/flutter_hooks(最新功能在prod-ready分支上)。~

~ 计划尽快发布 alpha 版本。 但是目前的实现在一定程度上是有效的~

可在此处获取https://pub.dartlang.org/packages/flutter_hooks# -readme-tab-

framework new feature

最有用的评论

只是想说——如果 Flutter 团队中的任何人想 1:1 讨论 Hooks,我很乐意解释为什么我们在 React 中采用它们背后的历史和技术背景。 此评论可能也很有趣: https ://github.com/reactjs/rfcs/pull/68#issuecomment -439314884。

所有100条评论

@Hixie @eseidelGoogle

这是对https://twitter.com/ericmander/status/1070024779015479302的后续跟进,其中包含了我的一些想法。 以防万一 Flutter 团队想在这个话题上进一步投资

@rrousselGit 前几天我在看你的flutter_hooks 包。 我不确定我们在 Flutter 中有哪些实现它们的选项,但我确实注意到您正在通过 HookContext 传递钩子方法,而在 React 中,钩子可以单独创建并用作一种“状态混合”。 关键区别在于flutter_hooks似乎要求所有钩子方法都内置到包中,而 React 允许钩子方法完全独立。

看起来这对于匹配 React 规范至关重要。 您对如何实现这一目标有任何想法吗?

编辑

HookContext已被删除,所有钩子现在都是静态方法。 所以下面的内容现在已经过时了


我的包这样做是为了自动完成,但这不是必需的。

代替

context.useSomeHook()

我们可以有

useSomeHook(context)

除了可发现性之外,这不会改变任何东西。

主要特点是

T HookContext.use<T>(Hook);

这使您可以从那里构建自己的钩子。 只要我们有这个方法,我们就可以完成 React 所做的一切。

但是由于钩子的特殊性,它会从自定义 linter 中受益匪浅。 目前无法提供哪些外部包。

我建议您投票(并将您的用例添加到)
https://github.com/dart-lang/linter/issues/697。 如果包可以提供自己的自定义 lints,那就太好了。

另一个问题是关于热重载。

这个让我很担心。 如果我们降低热重载体验,那将是可悲的。

可在此处获取https://pub.dartlang.org/packages/flutter_hooks

您能否详细说明将其烘焙到 Flutter 框架中比仅使用外部包的优势是什么?

关于热重载,我确实集成了它,使用起来很流畅。

唯一真正的问题是#26503,它似乎需要更改工具才能修复。 而且我没有足够的带宽来做这件事。


通过将钩子集成到 Flutter 中,我们可以为钩子带来多项小的改进:

  • 一些性能提升,不依赖于 StatefulElement
  • 重构/分析,目前无法使用analyzer_plugin 或非常有限

但我认为最大的原因是:Hooks 是 React 更大规模工作的一部分,即异步渲染。

如果 Flutter 想要继续 Async Rendering 路线(它应该),它将需要两件事:

  • 多个框架更改
  • 类似于钩子的东西

还有明显的社区方面。 一个月内有 270 颗星,并在https://github.com/trending/dart?since=monthly上占有一席之地,而我并没有真正为它们做广告——钩子绝对是引起社区兴趣的东西。

挂钩的目标是更改小部件的语法,以便人们可以将特定于生命周期的逻辑共享到 pub。

随着钩子的正式支持,这将成倍增加社区的努力。

好奇:你能解释一下你是如何让它为热重载工作的吗?

我扩展了 StatefulElement 而不是 ComponentElement,以便我可以访问reassemble
这允许下一个build调用更改挂钩列表,同时防止正常重建这样做。

基本上,如果previousHook.runtimeType != newHook.runtimeType位于给定索引处,则此钩子和所有以下内容将被丢弃。

“所有以下”部分是因为一个钩子可以使用其他钩子; 所以仅仅删除一个是不够的。
从持续使用一个月开始,它很少成为问题。

事实恰恰相反,热重载体验似乎增强了。 因为一切都在build方法中,所以热重载总是应用更改(而不是initState )。 一个典型的例子是AnimationController

final animationController = useAnimationController(duration: const Duration(second: 1));

这个钩子允许持续时间的平滑热重载; 这通常需要热重启。

我开始翻译我的主要应用程序的一部分以删除一些样板。 我不认为 Hooks 是一个特性,而是一个完整的模式。 他们有许多正在等待实施的主题,这些主题将极大地提高生产力,同时也有助于维护。 我从动画开始,移除控制器,并希望看到 Hooks for Firebase 或其他常见案例。

事实恰恰相反,热重载体验似乎增强了。 因为一切都在 build 方法中,所以热重载总是应用更改(而不是 initState)。

👍 在我第一次看到塞巴斯蒂安的提议几周后,我也意识到了这一点。

“所有以下”部分是因为一个钩子可以使用其他钩子; 所以仅仅删除一个是不够的。

我还认为在 JS 中启发式可能会有所帮助:如果热重载导致render (Flutter 的build )抛出(例如由于类型不匹配),我们会重置所有Hooks 状态并重试。 如果它再次失败,那么我们就失败了。

我猜你没有这个问题,因为强类型? 我不确定。 如果您开始使用字符串状态 Hook 进行编辑,然后更改代码以假定它是一个数字,会发生什么?

如果您开始使用字符串状态 Hook 进行编辑,然后更改代码以假定它是一个数字,会发生什么?

你的意思是从:

final counter = useState(0)

到:

final name = useState('foo');

?

如果是这样,打字系统正确地检测到类型改变了。 这是因为useState的类型实际上是useState<int> vs useState<String>

JS 在这里可能会遇到困难。

我真的不明白这可以为您节省什么。 一个易于理解的声明、initState 中的一个易于理解的行和 dispose 中的一个易于理解的行,在构建方法中被效率较低且完全不透明的行所取代(其中性能是危急)。 这似乎不是一个好交易。 您能详细说明这里的好处吗?

(也就是说,https://pub.dartlang.org/packages/flutter_hooks 似乎已经实现了这里的一切,所以我猜我们不需要向平台添加任何东西?)

Hooks 是 React 多年来面临的问题的一种解决方案。 由于 Flutter 从 React 中获得了很多灵感,它也引入了这些问题。

我很不擅长营销,所以我强烈建议观看 React Team 对这些的讨论。 他们的介绍从这里开始https://youtu.be/dpw9EHDh2bM。

随后是 Dan Abramov 之前/之后的并排比较(这里回答了上面的一些评论)。

关键是,他们在演讲中的所有内容也适用于 Flutter(包括他们关于 mixins 有害的博客 https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)


也就是说,钩子可以总结为以下朗朗上口的句子:

使用钩子,您可以“发布”更大比例的应用程序

这是因为可以完全从小部件中提取钩子并发布到 pub 以供其他人使用。 这对于StatefulWidget目前是不可能的,因为我们的逻辑与State生命周期相关联。

钩子的另一个作用是它改变了代码的分组方式。 我们目前有以下内容:

initState() {
  a = A(widget.foo);
  b = B(widget.bar);
}

didUpdateWidget(SomeWidget old) {
  if (old.foo != widget.foo) {
    a.foo = widget.foo;
  }
  if (old.bar != widget.bar) {
    b.bar = widget.bar;
  }
}

dispose() {
  a.dispose();
  b.dispose();
}

一切都在多个生命周期中混合和拆分。 很难看出我们是否忘记处置/更新变量。

使用钩子,它变为以下内容:

final a = useMemoized(() => A(foo));
useValueChanged(foo, () => a.foo = foo});
useEffect(() => a.dispose, [a]);

final b = useMemoized(() => B(bar));
useValueChanged(bar, () => b.bar = bar});
useEffect(() => b.dispose, [b]);

现在一切都重新组合了。 如果我们忘记处理/更新变量,这一点会变得更加明显,我们可以轻松地将该逻辑提取到一个静态函数中,该函数在内部完成所有操作。

挂钩符合 DRY。 我们编写一次创建/更新/处置变量的逻辑。 我们在任何地方都重复使用它,而不是复制粘贴它。

一个视觉示例(在 React 中):


总的来说,虽然可以在 Flutter 存储库之外实现钩子是令人难以置信的; 我认为它不应该保持原样。

这就好像 Flutter 只提供了Widget基类,并将StatefulWidget作为外部依赖:这没有任何意义。

Flutter 是一个框架,我们不想只提供基础知识

这意味着我们还希望:

  • 为将从它们中受益的所有框架方面提供挂钩
  • 与小部件检查器交互(当前 UI 未针对此类用例进行优化)
  • 重构/linter
  • 提供测试工具

这些需要大量的工作,并且可能需要在 Flutter 中进行多次 PR。
这与一个人在空闲时间维护的一个小包的范围相去甚远。

Hooks 是 DRY 并且是促进干净的代码托管的业务逻辑/状态混合。 另一种方法是跨代码库进行分段重复。

虽然我同意在纸上它更不透明,但图案是可重复和可理解的,因此在使用一天后它变得透明。 它就像任何其他编程模式一样:开始时不透明,使用时透明,但使用 Hooks 转换几乎是即时的。

在 react 中,许多开发人员认为 hooks 使许多原本依赖类组件的情况变得不必要。 因此,功能组件将在不久的将来成为舞台的中心。

我天真的问题是在颤振中支持功能小部件是否也有意义。 我知道目前不推荐使用功能小部件,因为它没有链接到它自己的元素并且缺乏自己的 BuildContext。 引入一种新的功能机制可以创建与类小部件相同的对象是否有益?

@ivenxu这也是我在另一个包中创建的东西: https ://github.com/rrousselGit/functional_widget

它是一个代码生成器,可以从函数生成一个类(带有键和所有内容),并且与钩子兼容以生成HookWidget

两者之和为:

<strong i="11">@hwidget</strong>
Widget foo(BuildContext context, {Duration duration}) {
  final controller = useAninationController(duration: duration);
  useEffect(controller.forward, [controller]);

  final value = useAnination(controller);
  return Text(value.toString());
}

它制作了一个从 0 到 1 逐渐变化的漂亮文本。
在 6 行代码中...
这将需要 30 多行带有常用小部件的行。

而且我没有计算自动生成的debugFillPropertiesoperator==覆盖。

@rrousselGit我知道您的功能性小部件工作,好东西,谢谢! 但是,我说的是颤振的原生支持。

顺便说一句,附加调试如何用于代码生成? 是否可以在 foo() 中设置断点并检查局部变量? 调用堆栈是什么样的?

有时,组件/小部件的功能范式比类对应物要简洁得多。 所以我正在寻找原生支持的功能小部件是否会有所作为的想法。

有了钩子,它变成了以下

我真的认为这里的“之前”图片比“之后”图片更清晰,更容易理解。 不过,显然,这是一种主观意见。

这就好像 Flutter 只提供了 Widget 基类,并将 StatefulWidget 作为外部依赖:这没有任何意义。

这样做实际上非常符合 Flutter 的分层理念。 我们将 StatefulWidget 作为框架中 widgets 层的核心 first-class 部分的主要原因是框架中的其他功能依赖于它(例如GlobalKey ),所以我们不能。 但我们尽可能地提取特征,使它们不在核心中。

我认为将它作为一个软件包提供给那些想要使用它的人是很有意义的。 如果我们可以在核心框架中做些什么来使其实现更好、更高效等,那么我们绝对应该这样做。

我认为这是一个很棒的项目,我很感兴趣地关注这个包。

也就是说,我并不完全清楚为什么这需要在框架中。 这似乎是一个非常有用的模式,有些人会喜欢,而有些人则不愿意使用。

@rrousselGit我怀疑你会取得很大的成功,继续将它作为一个单独的包进行开发,并为核心框架打开问题/PR,这将有助于使其更加成功。 如果您需要在框架中公开一些当前无法完成这项工作的东西,让我们来解决它。 如果框架中存在阻止它工作的错误,让我们让它工作!

FWIW,我说这与 flutter_svg 有类似的经历——这导致了对框架的一些贡献,并且能够作为一个包对很多人有用。

我刚刚意识到:
如果在 Flutter 中实现了钩子,那么 ComponentElement 就是一个不错的选择。

这意味着不是拥有一个新的 Widget 类型,而是让 StatelessWidget 和 StatefulWidget 都与钩子兼容。

这种方法可以允许两种语法共存。 考虑到两种语法都有其优缺点,这可能很有用


我现在可以在 Flutter 之外使用钩子。

我唯一担心的是,由于是非官方的,这将减少钩子的整体使用。 因此减少了 pub 上可用的钩子的数量。

考虑到钩子的最大优势是社区影响(可在 pub 上发布的自定义钩子),这会损害它们的实用性。

只是想说——如果 Flutter 团队中的任何人想 1:1 讨论 Hooks,我很乐意解释为什么我们在 React 中采用它们背后的历史和技术背景。 此评论可能也很有趣: https ://github.com/reactjs/rfcs/pull/68#issuecomment -439314884。

@Hixie@dnfield你们中有人联系过@gaearon 吗?

作为一个也经常使用 React 的 Flutter 用户,我可以说 Hooks 是 React 真正的游戏规则改变者。 我正在使用专门的钩子编写所有新代码,并在我处理它们时将任何旧类移植到钩子上。 将与组件的特定部分相关的所有代码放在同一个地方的好处是惊人的!

我认为如果努力评估该范式是否适合 Flutter 那就太棒了❤️

我错过了那个评论! 我刚刚联系了他们。

我必须承认我并不真正了解 Hooks 如何应用于 Flutter。 如果您想尝试使用它,已经有一些 Hooks 包(上面的这个错误中提到了一些)。 从根本上说,我真的不明白这个概念解决了什么问题。

有关我的观点的更多详细信息,请参见我之前的评论: https ://github.com/flutter/flutter/issues/25280#issuecomment -455846788, https ://github.com/flutter/flutter/issues/25280#issuecomment - 455847134, https: //github.com/flutter/flutter/issues/25280#issuecomment -456272076。

从根本上说,我真的不明白这个概念解决了什么问题。

Hooks 类似于状态混合,但没有混合的固有问题:没有变量冲突,并且可以多次使用。

Hooks 可以解决 Flutter 目前对有状态 Widget 的最大问题之一:处理所有这些控制器。

每个控制器都需要一个 initState、didUpdateWidget 和 dispose,它们_总是_以相同的方式实现。

我们无法将其提取到基类/mixin 中,因为我们可能需要多个。
因此,我们必须到处复制粘贴逻辑。
但这也不是很好。 很容易忘记一个步骤,比如覆盖dispose


钩子的真正问题是它们需要大量工作才能实现。

核心框架很简单。 但是由于它们独特的行为,它们需要与 linter 和重构工具进行深度集成。

为了实现最佳实现,可能需要一些语言特性,如元组/解构。

我觉得这并不能真正解决我上面的评论(https://github.com/flutter/flutter/issues/25280#issuecomment-455846788 和 https://github.com/flutter/flutter/issues/25280#特别是 issuecomment-456272076)。

嗯, @gaearon可能会比我解释得更好,但从我的角度来看:

  • 可读性参数取决于小部件的大小。 在较大的小部件上,钩子实际上是_更多_可读的,因为所有相关的位都在一起而不是分布在生命周期中。

  • 更少的错误。 他们的声明式 API 使得忘记更新/清理状态变得更加困难。

  • 关于“为什么他们应该成为核心?”,我喜欢 Dan Abramov 使用的隐喻。
    钩子之于小部件就像电子之于原子。
    当它们是用于构建更大事物的原语时,排除它们是没有意义的。
    这不仅仅是实现钩子。 整个框架都可以从中受益。
    它们可用于改进动画、表单等内容。

@Hixie ,你有没有花时间使用 Hooks,或者你是在用电子表格做决定吗? 我不确定 Flutter 团队有多少时间用于编写框架内容与为客户编写应用程序。 我怀疑为客户编写应用程序的人会从与框架开发人员略有不同的角度来解决这个问题。 即,这如何使我更有效并为我的客户提供更多价值,而不是如果它符合与现有解决方案相同的标准。

我最近在我们的一个新热门应用程序中引入了flutter_hooks 和functional_widget。

它极大地减少了样板代码,并且在将所有那些杂乱无章的有状态小部件重构为带有钩子的小函数之后,代码库缩减了大约 70%。

从更高级别的角度来看,带有一组钩子的功能小部件就像在 RX 中使用 combineLatest 组合多个可观察对象的命令式简化。 但是,与在序言中明确说明所有可观察对象并对其进行操作的函数不同,钩子更加灵活,因为您将它们编织到需要值的代码中。

但是,最终每个这样的小部件就像 RX 流中的一个阶段。

我当然不认为我们应该删除flutter_hooks。 我只是看不出我们(Flutter 团队)能提供什么更有价值的东西。

钩子之于小部件就像电子之于原子。

我认为如果我们遵循这个比喻,你应该将 Flutter 视为夸克,或者可能是强子。 Hooks 的电子是上面的一层。

@Hixie ,你有没有花时间使用 Hooks,或者你是在用电子表格做决定吗? 我不确定 Flutter 团队有多少时间用于编写框架内容与为客户编写应用程序。

我自己没有使用过 Hooks。 我确实用 Flutter 编写应用程序。 这样做时,我没有发现任何需要像 Hooks 这样的东西。 不过,如上所述,我不认为 Hooks 符合我的开发风格(我更喜欢看样板文件)。 我绝对不想从任何想要使用它的人身上移除 Hooks。

我只是看不出我们(Flutter 团队)能提供什么更有价值的东西。

为了使钩子真正有用,它们需要一个分析器插件和自定义重构选项。
只有有了这些,钩子才能真正发光。

Hooks 允许进行非常强大的静态分析,这对于典型的 StatefulWidget 来说是不可能的。
这使得管理小部件的本地状态更安全/更具可扩展性。 对我来说,样板减少只是一个奖励。

但:

  • 在非常低级的 API 上有很多工作要做。
  • 分析器插件接口不稳定,缺少,几乎未使用,团队不推荐(因为每个插件都执行一次分析)
  • Flutter 本身不使用插件 API,而是直接在服务器上实现

所以,作为一个社区维护的包,拥有完整的钩子工具的可能性非常低。

我认为如果我们遵循这个比喻,你应该将 Flutter 视为夸克,或者可能是强子。 Hooks 的电子是上面的一层。

如果 Flutter 是夸克,那它也是一个星系。

我们确实有像Layer这样的非常低级的类。
但也有大量像Switch这样的高级课程。

整个 Material 和 Cupertino 库可以被提取为包,然后是 Navigator,并形成相关的对象。 但他们不是。
Hooks 是在这些下面的几层。

无关,但我坚信 Material 和 Cupertino 不应该出现在 Flutter 核心库中,因为它会导致在超低级组件和 Material 高级组件之间缺乏良好的接口。 如果您曾经尝试过在没有材料库的情况下制作应用程序,那将非常困难。

Hooks 并没有真正减少样板文件。 (我们也不介意样板。)对我来说,主要价值是能够封装和组合有状态的逻辑。

const [value, setValue] = useState(0)
const debouncedValue = useDebounced(value, 1000)
const interpolatedValue = useSpring(debouncedValue, {
  friction: 10,
  mass: 20
})

能够在它们之间传递数据而不用担心它们是否包含状态,将这个逻辑提取到自定义 Hook 中,甚至多次应用同一个 Hook,非常具有表现力。 与类似 Rx 的方法不同,您实际上可以单步执行所有代码,而无需挖掘组合器。

为了使钩子真正有用,它们需要一个分析器插件和自定义重构选项。
只有有了这些,钩子才能真正发光。

我同意我们需要支持这一点。 不过,这应该是针对 Dart GitHub 项目的一个错误。 当然,我希望我们将 Flutter 特定的 lints 从 Dart 代码库中移出。

整个 Material 和 Cupertino 库可以被提取为包,然后是 Navigator,并形成相关的对象。 但他们不是。

是的,这可能也是真的。 我们不这样做是有实际原因的(例如,它会使“Hello World”变得复杂,从而使开始使用 Flutter 变得更加困难),但我们仍然可以考虑它。 不过,这又是一个单独的问题,如果我们想考虑它,我们应该单独提交。

对我来说,主要价值是能够封装和组合有状态的逻辑。

这确实非常有价值。 尚不清楚它是否必须在核心框架中。

这确实非常有价值。 尚不清楚它是否必须在核心框架中。

这将允许弃用一些核心状态混合。

例如AutomaticKeepAliveClientMixin处于一个奇怪的位置。
我们必须调用super.build ,这非常违反直觉。 并且super.build返回null ,这不适用于不可为空的类型。
但是作为一个钩子,这两个问题都解决了。

同样,我们不需要同时使用SingleTickerProviderStateMixinTickerProviderStateMixin ,因为钩子可以根据需要多次重复使用。

或者,钩子有一个重要的社区方面。 如果它们不是核心,人们就不太可能创建和共享自定义钩子。

我们实际上不会在核心框架中_使用_钩子,因为恕我直言,它们使事情的可读性降低。 因此,将它们放在核心框架中无助于删除其他代码。

或者,钩子有一个重要的社区方面。 如果它们不是核心,人们就不太可能创建和共享自定义钩子。

如果 Hooks 好用,那么无论是来自 Flutter 开源项目还是 Hooks 开源项目,人们都会使用它们。

我们实际上不会在核心框架中_使用_钩子,因为恕我直言,它们使事情的可读性降低。 因此,将它们放在核心框架中无助于删除其他代码。

或者,钩子有一个重要的社区方面。 如果它们不是核心,人们就不太可能创建和共享自定义钩子。

如果 Hooks 好用,那么无论是来自 Flutter 开源项目还是 Hooks 开源项目,人们都会使用它们。

这听起来不错。 Hooks 可以作为一个独立的开源项目运行。 核心团队是否会优先处理来自 Hook 项目的核心变更需求? 恕我直言,钩子项目的成功非常重要。

什么是“核心变更要求”?

什么是“核心变更要求”?

开发工具。
主要是关于分析器插件,但也可能是 Flutter/devtools 上的插件系统。

那里涉及很多工作,社区无法完成。

我们实际上不会在核心框架中_使用_钩子,因为恕我直言,它们使事情的可读性降低。 因此,将它们放在核心框架中无助于删除其他代码。

恕我直言,但这听起来有点无知。 你真的应该试一试! 对于状态的每个方面,它们比使用 initState/dispose/setState/build 更简洁。

Flutter 完全是关于组合的,使用钩子自然遵循组合模式,而另一方面,使用 mixin 与组合相反。

我想我们将继续考虑使分析器可扩展成为重中之重。 不幸的是,还有许多其他更高的优先级,例如改善基本第一小时任务的体验以及为默认情况下不可为空的更改实施工具。

也就是说,我不明白为什么非 Google 员工不能做到这一点。 Flutter 和 Dart 都是开源项目,很多东西都是由非 Google 员工实现的。

也就是说,我不明白为什么非 Google 员工不能做到这一点。 Flutter 和 Dart 都是开源项目,很多东西都是由非 Google 员工实现的。

我们谈论的是多个项目的几个月工作,对于团队认为“低优先级功能”的事情,有许多重大的突破性变化。 受影响的项目级别非常低,几乎没有公开文档。

所需的投资和被拒绝/被卡在中间的风险太高了。 尤其是当我们没有从中获得任何资金时。

所以虽然我理解你的观点,但这是极不可能的

有没有关于它的路线图? 您是否知道钩子是否以及何时可以扑动?

如果 Flutter 在 1 年内成功(我希望它会成功),每个尝试加入 Flutter 的 React 开发人员都会在发现这里没有一流的 hooks 支持时尖叫。

Flutter 团队复制基于类的 React 模型做出了很好的决定,但现在这已成为过去,功能组件和钩子是未来,Flutter 团队如果想保持水平,就应该意识到这一新现实.

正如 cgarciae 所说,React 社区正朝着钩子和功能组件前进。 这让 Flutter 看起来过时了,因为编程模型和生产力无法与其他跨平台开发解决方案相提并论。

最近尝试了 react 和 flutter,在比较下,flutter 中缺少的钩子在生产力、一致性和可维护性方面留下了一个很大的漏洞。 它不会帮助组织在新项目中采用 Flutter,或者说服人们从 React 基础解决方案转向 Flutter 值得付出努力。

我完全理解当有人构建工具集、低级框架实现时,样板代码似乎不是什么大问题。 但是,如果应用程序开发人员需要编写大量“不必要”的样板来使某些东西正常工作,那么效果是不希望的。

取决于人们看待特定问题的角度,权衡/平衡可能看起来不同。 不提供适当的工具或某些功能可能会使框架内部实现看起来更干净,但它只是将责任转移给框架消费者,使客户端实现比它必须的更复杂和笨拙,这反过来无助于适应给予框架。 对于应用程序开发人员来说,生产力和一致性确实很重要,统一的方法/标准对于团队合作和社区非常重要。 整体方法应始终考虑人们生活在围栏的另一边。

Hooks 应该很容易实现,但功能性小部件更难实现。 这将需要 Dart 中的联合类型,因为您需要表达如下内容:

'小部件 | 小部件函数(BuildContext)'。

功能小部件更难。 这将需要 Dart 中的联合类型

这还远远不够。
还有一个问题:

  • 如何将参数传递给函数? 咖喱?
  • 一旦在小部件树中,两个功能小部件应该有不同的 runtimeType
  • 如何使用 devtools 插入小部件? (小部件的debugFillProperties方法)

我已经考虑了很长时间。 对于功能性小部件,我没有看到任何简单的解决方案。
我唯一能想到的就是代码生成和分析器插件的组合(有适当的定义和东西)。

实际上,代码生成部分使我们退出了您的functional_widget 包。 它只会在审查代码时产生很多跳跃。 所以,这对我来说不会飞...

它只会在审查代码时产生很多跳跃

因此我提到了一个分析器插件。

使用自定义分析器插件,我们应该能够:

  • 检查是否直接使用该函数并且该类是首选
  • 有一个适当的“去定义”/“偷看”/...,这样点击类重定向到函数

这应该可以解决所有问题。
但是我们要回到“钩子需要一个适当的分析器插件系统”。

也许不需要引入functional小部件:

假设只允许在StatelessWidget build方法中使用钩子。

从技术的角度来看——它是类还是函数并不重要(就我现在所能想到的)。 重要的部分是你不要混合有状态的类和钩子。 在底层,如果在build方法中调用并正确管理钩子,flutter 渲染器仍然可以计算钩子及其顺序。

将来,如果可以创建功能性小部件,这并不是重大变化。 此外,它将是向后兼容的,因为它不会破坏现有的应用程序。

我认为在StatelessWidget中使用钩子没有任何明显的问题,这会带走使用钩子(如组合)的好处。

但是,我不确定它与useCallback之类的情况相比如何,与无论如何使用实例方法的能力相比

“我们必须添加这个,因为 React 开发人员会生气”的论点是无效的。 (参见 500 多条评论 JSX 讨论)。

Flutter 就是 Flutter,它有自己的语言和设计模式。
我认为我们不应该仅仅为了让 React 开发人员更熟悉而向框架添加东西,如果他们愿意的话,他们可以使用这个包。

我阅读了讨论,我认为@Hixie作为 Flutter 框架开发人员只是看到了他的问题(实现和......东西)并且对 Flutter 应用程序开发人员(这是他的目标受众)如何思考和使用它的看法较少他们的发展。

我作为一名 android 开发人员遇到了这样的问题,尝试为我的应用程序开发一个出色的后端结构,却不知道我的应用程序用户在看到我丑陋的 UI 和难以理解的 UX 时会有什么体验。

正如其他人之前所说,钩子很棒。 不管是谁介绍的,无论是邪恶的FB还是苹果。
它只是工作。 它将状态逻辑及其生命周期分开。 我们可以在其他钩子之上制作一些有用的钩子,从应用程序状态发布独立的小部件、api、工具……,在其他项目中重用我们预先制作的钩子……

而且我认为,如果在pubspec.yaml中预先添加一行来导入材料或 cupertino 很难创建一个简单的 hello-word 应用程序!!!,这里没有什么可讨论的。
但是由于StatefullWidget对于 Flutter 生态系统非常重要,因此 hooks 及其相关的内容在 Flutter 及其生态系统和 Flutter 开发者社区的未来更加重要。

试想一下,flutter 应用程序开发将如何变得更简单、防错误、易于理解、可扩展、可读、可调试、可移植……(任何好的瘦加能在这里请😁)与核心直接支持的钩子(或其他东西)在核心和一些复杂的小部件(如材料,...)之间的中间,特别是官方的。

为了更好地理解我的观点,看看使用 java 和支持库的 android 开发是多么糟糕,以及 Kotlin 是多么的花哨和甜蜜。
它还为 Coroutines、Flow、AndroidX + Kotlin 支持、新的 ui 系统 (Compose) 等其他好东西打开了大门
甚至它推动了 Java 社区在他们的生态中启动和实施 kotlin 期货。

所以请把任何缓冲区放在钩子里,然后做出反应,然后去做

缺乏官方 hooks 支持是我没有从 react native 切换的唯一原因。

Hooks 让生活变得如此简单,以至于我不想再用旧的方式来写它了

“native hook”的惊人部分是我们可以将许多内部 StatefulWidget 或......移植到这个简单易懂的形式。
Flutter 团队也宣称 Flutter 和 Dart 易于使用、易于理解和快速学习曲线。 使用 Native Hooks,所有这些都可以实现并且更好。
并且 (initState/dispose/setState/build) 方式不在此路径中。 (我们可能在平台后端需要它们,但对于想要使用代码来描述他们的想法(不是复杂逻辑)的新开发人员甚至设计师来说不需要)

我在这个线程上的阅读是,有很多人对在 Flutter 中使用钩子感到兴奋,还有一些关于钩子的开放性问题:

  • 性能影响/测量 - 特别是始终调用函数的效率与看到该状态可以在不调用新函数的情况下重用。
  • 能够(无法?)很好地使用全局键和/或有效地在树周围移动。
  • 特定于钩子的分析器支持/lints。
  • 能够像普通的旧有状态/无状态小部件一样使用热重载。
  • 可能需要额外的框架 API。

我确信所有这些问题都可以解决,但解决它们并非易事。 问题不在于“钩子应该在框架中还是应该在单独的包中”。 在某种程度上,这个决定已经做出——flutter_hooks 已经在 pub.dev 上提供了一年多,并且似乎是一个流行的包。 就是要让 flutter_hooks 成为真正完美的体验,除了已经完成的工作之外,还需要一些重要的工作和投资。

已经为核心框架类完成了很多工作,并且花费了数个工程团队以及多个开源贡献者数年时间才将其完成。 有时似乎有一种错觉,“如果我们将它合并到 repo X,所有未解决的事情都会解决!” 但是这些事情发生的方式是那些对它们感到兴奋的人去做实施它们的工作。 @rrousselGit已经在这方面做了很多工作,看起来其他几个贡献者也在 hooks 存储库中。

简而言之,我想说的是 - 如果不在 Flutter 存储库中,hook 可以很好地工作,在 Flutter 存储库中不会加速解决其突出的问题,并且任何想看到 Hooks 在 Flutter 中工作的人完全有能力做到这一点。

好吧,在我看来,钩子应该是一种类似于同步/异步生成器的语言特性。
它根本不必与颤振有关。

钩子解决的问题是:

  • 声明式和反应式状态管理。
    State势在必行
  • 状态组合来修复 DRY。
    所有这些 XXController 都需要创建+更新+处理,并且无法分解该逻辑。
  • 可读性,因为它不涉及函数/小部件的无限嵌套来实现该结果

但另一种语法可能是:

class MyWidget extends HookWidget {
  const MyWidget({Key key, this.title}): super(key: key);

  final String title;

  Hook<Widget> build() hook* {
    final (flag, setFlag) = yield false;
    final (animationController) = yield* useAnimationController(duration: const Duration(seconds: 2));

    // TODO: do something with animationController
    return CheckBox(
      value: flag,
      onChanged: setFlag,
    );  
  }
}

Hook<AnimationController> useAnimationController({required Duration duration}) hook* {
  final (tickerProvider) = yield* useTickerProvider();
  final (animationController) = yield AnimationController(duration: duration, vsync: tickerProvider);
  animationController.duration = duration;

  return animationController;
}

这样就达到了和flutter_hook一样的效果,但是独立于flutter,有多个flutter_hook做不到的优化因素。

这基本上允许_anything_使用钩子,而不仅仅是小部件。

这是一个有趣的想法。 听起来那里有多种语言功能请求(类似于 Go 中的defer ,类似于 C++ 中的确定性析构函数,某种将函数隐式组合成对象的能力)——但这并不是真正正确的地方跟踪。

我认为通过在 Flutter 中没有钩子之类的东西来识别人们遇到的痛点是很有价值的,并且在没有选择解决方案的情况下专门提出有关该痛点的问题。 然而,完全有可能这些痛点中的一些可能只是框架为了实现有关性能、质量或可用性权衡的目标而强制执行的事情(例如,我们可以使痛点 X 在使痛点 Y 变得更糟的成本)。

就目前而言,我将关闭这个问题。 有一个很好的包可以解决这个特定的解决方案,并且解决方案本身还没有准备好在其当前状态下合并到框架中(出于上述原因),并且不清楚该解决方案是否真的会受益于被合并到框架中。

@dnfield Hooks '作为一个家庭'工作得很好,只有当你涵盖了所有主要的需求领域时。

如果您在状态更改后需要某种效果时碰壁,那么使用状态挂钩将毫无用处。

任何设计的钩子都需要在渲染器的核心内部进行重大更改或引入一种新的核心组件。

这样的努力对于一种钩子(痛点)来说是不值得的,所以我想说不可能将这个问题作为一组较小的问题来解决。 单个钩子不值得从有状态组件切换,并且肯定不值得在渲染引擎中引入重大更改。

如果这个问题的意思是说“我希望 Flutter 更像 React 但也像 Flutter”,我不确定它的可操作性 - 而且 GitHub 问题可能不是跟踪此类事情的正确方法,即使我们授予这样的事情进行跟踪是有意义的。

换句话说:如果这是一个重写 Flutter 核心的请求,这不是处理它的正确方法。 我不确定处理它的正确方法是什么,但它可能会从分叉存储库并进行大量工作开始。

当钩子被介绍给不了解钩子并维护其他技术的人时,我有一种奇怪的感觉。

似乎回复通常是“如果你希望 X 像 React,请使用 React”。

我的观点是,钩子是许多需要重用复杂数据逻辑的前端问题的通用解决方案。 它更像是一个可以用于任何技术的想法。

然而,钩子在其价值完全可见之前需要在考虑数据流时进行某种转换。 我认为在其他框架中“请求”钩子时这可能是一个大问题。

我认为 React 团队在尝试解释 hooks 方面做得很好,但它可能会产生一些阻力,因为它使 hooks 看起来与 React 密切相关。

我认为 React 经常被提到 next hooks 只是因为这是它们被发明的地方。 React 也是关于钩子的最佳信息来源(到目前为止)。

一般来说——我将“痛点”描述为不能直接在 Flutter 中使用数据逻辑的组合模式。 钩子只是允许这样做的一个例子。

我也明白引入钩子是一项艰巨的任务,我必须说我不愿意承担。

我个人不理解“社区试图解决这个问题,所以我们不需要做任何事情”的说法。

社区修复并不意味着 Google 不能做得更好。
以集合内的表达式为例。
社区可以通过分叉 Row/Column/ListView/... 来“修复”它以支持null
但谷歌通过修改语言来修复它。

此外,Flutter 本身也这么说:Flutter 的灵感来自 React。
这可以从如何使用 Widget 的很大一部分基本上是如何使用 React 类中看出。

虽然这并不意味着 Flutter 应该匹配 React,但我希望 Flutter 团队至少密切关注 React 更新及其生态系统。
除了社区论点之外,这至少应该为“我们不希望框架中的钩子”提供更好的答案。

@rrousselGit @pie6k

我个人发现 Hooks API,它在 React 中的方式,有点难以掌握。 我更喜欢生命周期方法及其描述性命名。

我知道这是一个“我的问题”,但我支持@dnfield评论说我们也许应该解决问题并以不同的方式实施解决方案。

此外,我很少遇到两个单独的小部件需要共享相同逻辑的情况(这就是我理解的 Hooks 的主要好处?)

关于 Flutter 受到 React 的启发,这没关系,但这并不意味着它将永远遵循所有 React 模式和架构。
我在使用 React 时遇到的主要问题之一是做某事有很多不同的方法,而且“推荐的做法”每天都在变化。 我知道这是软件工程的本质,但我仍然认为,如果现有方法有什么不好的地方,应该尝试改进它们……而不是堆积不同的方法来实现相同的目标。

我希望这能为您提供一个稍微不同的视角,并且不会冒犯任何人。

非常尊重您对 Flutter 社区的贡献。

很遗憾这个问题被关闭了。 这使得 Flutter 团队似乎对钩子一无所知,或者不关心设计模式的当前状态(我希望不是)。 这对任何技术都不好,投入数百小时的人想知道自己使用的是一流的或可以与之媲美的,而现在最有成效的模式是钩子,而Flutter落后了,但是它甚至没有被维护者考虑或忽视,而是支持一个令人信服的更好的替代方案。

@cgarciae没有人是无知的, @dnfield明确表示——与其试图挤进现有的解决方案,不如为你遇到的相应问题打开问题,Flutter 团队将对它们进行评估,并可能提出更适合 Flutter 的解决方案。

这是社区一直强烈要求并将要求的功能。 如果问题不是正确的地方,你们是否有功能请求来注册社区的要求?

@SpajicM我完全可以回答“不”。
我不同意的是目前的理由。

一些注意事项:

这些应该给一些关于钩子的深入讨论。 不一定要实施它们,但至少要探索它。

最后归结为沟通问题。

  • Flutter 团队是否按照 Dan Abramov 的要求联系了 React 团队?
  • 团队是否在 React 中尝试了钩子?
  • 是否考虑了钩子解决的问题的替代方案?

作为一个社区,我们对此一无所知。

再往前推,当 React 团队自己提出向 Flutter 团队解释这些问题是什么时,我认为社区被建议将这个问题拆分为“hook 解决了什么问题”是很讽刺的。

@rrousselGit我认为它背后的想法是直接从用户那里听到 Flutter 特定方面的问题,并将其作为起点,而不是采用 React 的推理,这并不是说他们的输入没有价值。

我的观点是,最好有一种方法可以清楚地区分小部件的逻辑和设计 -所以我同意有一个问题需要解决,但是 React 这样做的方式让我有些困惑,我希望 Flutter 未来的解决方案有一个不那么剧烈的范式变化/学习曲线。 我没有任何想法,但我敢肯定,如果思想开放且不拘泥于现有的 Hooks 实施,社区和团队可以想出一些东西。

我想下一步是打开一个名为:

_“Flutter 需要一种更好的方法来隔离小部件逻辑”_

我认为https://svelte.dev/有一种不同且更好的方法来解决这些问题。 作为第一步,Flutter 团队应该意识到存在问题,我们需要解决方案。

我是 Flutter 的新手,我觉得 API 有很多样板。 认为 Dart 有一个通用的,感觉就像我在 Go 中编程,在那里复制和粘贴更常见/更容易。 我希望当 NNDB 登陆时会有一个巨大的重构,尤其是充分利用扩展方法。 通过扩展方法之类的渐进式 API 抽象可能值得探索。

恕我直言,Flutter 团队过于热衷于“一切都是 Widget”的想法。 小部件非常适合视觉项目和布局,但不适用于数据获取/处理。 而不是繁琐的FutureBuilderStreamBuilderTweenAnimationBuilder等。我更喜欢功能性API:

Widget build(BuildContext context) {
    final a = useFuture(someFuture);
    final b = useStream(someStream);
    if (a.value == null || b.value == null) {
        return CircularProgressIndicator();
    }

    final value = a.value + b.value;
    final smoothedValue = animate(value, duration: Duration(milliseconds: 100), curve: Curves.easeInOut);

    return Slider(
        value: smoothedValue
    );
}

事实上,Flutter 已经在某些地方使用了 hooks。 所以而不是更多的“飘扬”

MediaQueryGetter {
    builder: (BuildContext context, MediaQueryData data) {
        ...
    }
}

您可以使用
final data = MediaQuery.of(context);

不幸的是,这种机制(获取时订阅)只能与 InheritedWidget 一起使用。

我不确定我们可以在这方面取得多少进展,但我确实想解决我在这里看到的几点:

  1. 评估 _solution_ 比评估 GitHub 问题中的 _problem_ 困难得多。 当您提出解决方案时,要获得接受度要高得多。 您必须证明该解决方案结构良好,值得付出努力,并且它解决了与其所需努力水平相称的一些实际问题(不仅包括初始原型设计和实施,还包括持续的维护和质量) . 换句话说,我们讨论一个围绕管理数据和小部件状态的特定痛点的问题可能会导致许多解决方案,其中一些可以被采用到框架中,其中一些可能成为第三方包,还有一些其中会很好,但成本太高了。

  2. 致力于 Flutter 和 Dart 的 Google 工程师一直在努力改进它,而且通常都很忙。 许多为 Flutter 做出贡献的非 Google 工程师也是如此。 这个 bug 的关闭并不意味着没有 Google 工程师会致力于让钩子在 Flutter 中更好地工作。 hooks 包由社区成员拥有这一事实不会以某种方式降低包的质量或其价值 - 也不会降低解决此错误中发现的一些缺点的能力,例如更好地支持分析/linting

  3. 接受或拒绝功能有不同的原因。 有时是该功能非常酷,但与架构不太匹配。 有时,该功能表面上看起来不错,但有一些尚未解决的主要缺点。 有时该功能很棒,它需要付出足够的努力,但付出的努力超过了它为用户提供的价值,或者会占用其他更有价值的功能所需的时间。 特别是对于 Flutter,有时只是因为我们有一个高度分层的架构,可以通过第三方包进行扩展,而且很多时候保留铺设并允许第三方做伟大的工作比在框架中包含一些新东西更好。 我们自己在包中执行此操作,例如位于https://github.com/flutter/packages/tree/master/packages/animations 的新动画包。

最后,我一直在这件事的另一端。 我自己是一个包维护者,我拒绝了一些功能建议,这会使我的包更容易维护或开发。 我唯一的建议是,如果您有自己喜欢的出色软件包,请继续努力。 其他人也会认出它并提供帮助,无论它是否包含在这个 repo 中。

Android 新的 ComposeUI 具有类似钩子的状态:(preview2)

val state = remember { CardDesignerState() } // react: let state = useState(CardDesignerState())
val thing = stateFor<T?> { null } // react: let thing = useState()

iOS 新的 SwiftUI 也有类似的东西(但它在内部):

// from https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions
Image(systemName: "chevron.right.circle")
                        .imageScale(.large)
                        .rotationEffect(.degrees(showDetail ? 90 : 0))
                        .scaleEffect(showDetail ? 1.5 : 1)
                        .padding()
                        .animation(.easeInOut)

想象一下 iOS 的 swiftUI 使用 onCreate, onInit, onUpdate, onExit, ... 🤢

但是最好的框架(当然是Flutter)仍然抵制太使用钩子的想法,为什么?
因为它看起来像 React/ReactNative !!! 或维护团队的一些惊人报价:

我更喜欢看样板

显然反对使用声明性语法编写 UI 的颤振想法

我认为hooks mixin是一个很好的解决方案。
在我们喜欢的任何地方都有钩子,不要接触旧式开发人员的代码和框架。
但是这个想法和任何其他伟大的想法都需要与核心库进行高度集成,很高兴看到核心维护者一流地支持这些特性。
我也可以想象钩子集成后的未来,许多顶级框架库(如材料)大力支持甚至在其中使用。

Flutter 可以更漂亮:

// this is just scratch, not a complete and true use-case
build(context) {
    final angle = useAnimation(2*PI, 0, 5 /*seconds*/);
    return Image.from(myAwesomeImage)
        .padding(8)
        .scale(2.5)
        .rotate(angle);
}

@HKhademian完全同意。 缺乏对任何类似钩子的东西(或其他术语 - 任何允许正确组合可重用业务逻辑的东西)的一流支持实际上是我在上一个项目中没有选择 Flutter 的唯一原因。

2. hooks 包由社区成员拥有这一事实不会以某种方式降低包的质量或其价值 - 也不会降低解决此错误中发现的一些缺点的能力,例如更好地支持分析/linting

社区成员会做鬼包吗? 是的。 谷歌会去幽灵核心 Flutter 吗? 好吧,根据你在这里的立场,这似乎是一种可能性。 断言核心 Flutter 与社区软件包一样可用于消费是……最好的说法是错误的,最坏的情况是谎言。

此外,已经一次又一次地证明,除非有来自@Hixie的支持,他已经证明了使用个人偏好来覆盖社区请求的亲和力,否则社区想要或需要什么并不重要。 没有买入,就不会发生。 如果不是这样,那么决策过程就太不透明了,看不到其他情况。

让我感兴趣的是,决定功能是生是死的人似乎没有构建移动应用程序的业务经验。 这种观点将始终对将哪种糖混合到 Flutter 中产生最深刻的见解。

我认为我们需要一个正式的流程来决定 Flutter 中的哪些功能,从而更加重视社区为决策过程做出贡献。 显然不是 100%,但现在感觉它大约是 5%,而且是 5% 的超级不稳定。 @timsneath

评估解决方案比评估 GitHub 问题中的问题要困难得多

React 团队在本期一开始就提出解释他们背后 Hooks 的原因。
这应该完全涵盖了解释为什么需要钩子的需要,因为没有人比 React 团队自己更适合解释钩子。

卢克,我尊重并欣赏你的热情,但请停止人身攻击。 我们都在努力尝试建立最好的平台,根据资源容量、用户研究、利益相关者输入、客户需求和社区输入进行各种权衡。

请善待。 https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md

没有针对人身攻击的意图,感谢您的反馈。 根据 CoC,也许我们应该聚在一起讨论我想要传达的内容。

不同意的人应该聚在一起,试着理解彼此的观点,并努力找到一个能解决每个人关注的设计。

是否有任何更新,或者我们可以在哪里协调解决方案? 我在 Flutter 项目中遇到了这个问题,尤其是动画和过渡。 挂钩有助于将此initStatedispose状态等封装到单个函数中,并且只需使用该函数而不是设置所有内容。

至少对于 Flutter 的外部观察者/用户而不是插件维护者来说,确实存在一些合理的担忧,他们不会觉得他们被倾听,例如 Flutter Overlays 的另一个问题 (#50961 ),它们似乎被认为对用户来说不是真正的问题,而不是维护者。 再说一次,不要不尊重维护者,这只是我作为随便阅读这个问题线程的人的看法,我对任何其他可能不同的问题不太熟悉。

话虽如此,我们如何才能更普遍地思考用户似乎合法拥有的问题的解决方案? 我知道 Rust 有一个有趣的 RFC 系统,似乎可以很好地设计新的解决方案。

@satvikpendem这里的 RFC 流程是提交一个描述问题的错误,然后在清楚地描述问题后讨论可能的解决方案。

到目前为止,似乎已经解释了问题,所以现在可以根据您的评论讨论解决方案。 我们接下来可以做什么,还有其他解决方案比钩子或类似的东西更好吗?

如果你让我知道错误#我可以让你知道问题是否足够详细地描述了一个问题,以便开始讨论可能的解决方案是有意义的。

@hixie https://github.com/flutter/flutter/issues/51752

我还认为我们应该跟进 Dan 的评论: https ://github.com/flutter/flutter/issues/25280#issuecomment -456404333
这次讨论的结果是什么?

嗨 Remi,非常感谢错误 #51752。 我知道你在这个领域投入了大量时间,并在这里贡献了非常有价值的软件包。 谢谢你x1000!

重申@dnfield的评论,似乎我们还没有围绕问题空间及其意义达成一致。 上面的错误是朝着该目标迈出的有用一步,和/或讨论解决问题的潜在方法。 您在这里的后续问题假设与 React 团队的对话是从这里开始的第一步,但如果我们还没有就问题空间达成一致,那似乎还为时过早。

也许我们可以展示具有此类问题的应用程序的完整示例,这些问题将通过钩子修复。 我们不一定要用钩子重写它们,而只是显示有多少复制粘贴。 @timsneath 是否适合讨论? 还是你会想别的? 我试图弄清楚如何尽可能清楚地显示问题空间。

:wave: @timsneath
老实说,我不太明白为什么与 React 团队讨论会为时过早。

即使没有社区要求钩子,与 React 团队的讨论仍然非常有价值。
React 与 Flutter 有很多相似之处,并且他们在处理类似 Widgets 的对象方面有几年额外的经验。

与他们讨论只能给双方带来好处。

举个例子:
创建 flutter_hooks 后,Dan 联系我讨论我如何处理 hooks 的热重载。
我的回答是“几乎没有什么可做的,因为 Flutter 使用了类型化语言”。
几个月后,他们通过使用 Babel 生成类似于类型的东西来改进 React 的热重载

我敢肯定,如果 Flutter 和 React 团队讨论,许多这样的交互可能会发生,并且两种技术都会进步。

一些开放式问题可以在没有特定问题的情况下提出:

  • 为什么是钩子?
  • 为什么是悬念/并发模式?
  • 为什么是门户?

@satvikpendem这里的 RFC 流程是提交一个描述问题的错误,然后在清楚地描述问题后讨论可能的解决方案。

这是否意味着任何不是错误的讨论都会被忽略? 我认为 RFC 的目的是针对不是 bug 的东西,但通常是为了扩展更主观的东西,例如开发人员体验或工具语义。

如果有人说,“我认为我们应该有钩子,因为它们在多个框架中变得惯用并且人们报告改进的体验和生产力”,这是一个有效的讨论,但它不是一个可重现的错误。

我绝对确定没有人说“不是错误的讨论将被忽略”。 来吧,让我们真诚合作,相互尊重,以礼相待。 我们都在这里尽了最大的努力,但还有许多其他竞争错误、设计和想法在争夺我们的注意力:)

@lukepighetti具有讽刺意味的是,“我们应该有钩子,因为其他框架有它们”实际上正是我们试图避免的那种对话——因为它导致了一种根据定义针对其他框架的需求进行优化的设计。 在这个过程中描述我们试图在 Flutter 上下文中解决的问题真的很有帮助,因为这有助于我们就问题是否相同、解决方案是否应该相同等达成一致。

@rrousselGit——当然,我们可以与 React 团队进行一些有用的一般性对话。 我欢迎这个提议,也许我们应该在某个时候这样做。 当然,他们在这支球队中有很多聪明才智,而且他们的报价非常亲切和友善。 现在,我们主要是因为您告诉我们而与他们交谈,而不是因为我们有足够的信息来讨论具体问题:)

另外,提醒一下:“Flutter 团队”就是为 Flutter 做出贡献的人。 一些贡献者为 Google 工作,一些贡献者在管理项目架构方面发挥着更重要的作用,但你也是 Flutter 团队的成员,其他任何为项目做出贡献的人也是如此 :)

一如既往——感谢您耐心地讨论这个问题,感谢您对社区的贡献,继续在这个领域以创造性的想法推动项目向前发展!

澄清一下,我们使用的“错误”是一个通用术语,指的是任何可能导致更改的事物。 可能是一个用例,可能是一个想法,可能是一个逻辑错误,可能是文档中的拼写错误,网站上的一些令人困惑的东西,等等。

也许我们可以展示具有此类问题的应用程序的完整示例,这些问题将通过钩子修复。 我们不一定要用钩子重写它们,而只是显示有多少复制粘贴。

@satvikpendem如果问题是“我的应用程序有太多样板”,那么这肯定是您应该提交的错误,我们可以讨论如何改进问题。 在此处提及错误#,以便我们知道继续在您的错误中进行对话。

感谢@Hixie 的评论。 我的问题被@rrousselGit提到的相同错误(#51752)广泛涵盖,因此根据我在该问题中阅读的内容,我认为我没有更多要添加的内容。

@timsneath我不确定我是否理解您对@lukepighetti的评论,因为感觉就像我们已经在 Flutter 的上下文中多次描述了这个问题,例如这个问题、#51752 等。 我们还需要包括什么? 我们如何帮助您更了解这个问题空间,以便如果我们确实与 React 团队或其他人交谈,您将有足够的知识来提出有根据的问题,正如您所说的那样?

我同意我们不应该仅仅因为 React 拥有它们而从其他框架复制东西,因此查看其他解决代码重复问题的解决方案可能会有所帮助。 Vue 的创始人@yyx990803在 Vue 的 RFC (https://www.github.com/vuejs/rfcs/tree/function-apis/active-rfcs%2F0000-function-api.md) 中发布了他的一些想法,这将很有帮助通过。 仔细阅读有关解决了哪些问题以及为什么他们对基于类的 API 有不同意见的部分,对通读很有用。

感谢您澄清@Hixie ,我误解了“错误”的这个更广泛的(可能是内部的?)定义。

@timsneath我不确定我是否遵循。 另一组人,核心反应开发人员,已经识别并传达了一系列问题,在架构相似的框架中创建了解决方案,并且跨多个框架的许多前端团队都报告了成功。 我没有看到任何迹象表明这是“问题前的解决方案”GitHub 问题。 @Hixie似乎不同意有问题需要解决,而且这似乎是基于风格或维护选择,而这些选择并未反映开发人员体验的好处。 我怀着最大的敬意说,同时试图了解不情愿此 RFC 的来源。

我通常不推荐鹦鹉学舌功能,但钩子的 RFC 并非没有具有充分理由的现有技术。 核心反应团队提供现有技术,我们提出的任何理由都将重复他们可以和已经传达的内容。 @rrousselGit似乎在提倡你们与他们开会讨论这个话题,因为他们可以提供比我们在 GitHub 问题中更多的见解。

作为一个元问题,对我个人而言,如果有一个具体的流程可以将广泛的 RFC 纳入社区提出的flutter/flutter路线图,那将会很有帮助。 我们有一种说法,外部利益相关者是那些想要完成困难和必要工作的人,这就是为什么他们必须被纳入并认真对待。 flutter/flutter上下文中的外部将是非团队、非谷歌、非 GDE。

我已经在我们的 wiki 上记录了我们的RFC 流程

在颤动/颤动的背景下,外部将是非团队、非谷歌、非 GDE。

根据定义,如果您提交 RFC,那么您就是团队的一员。

我的问题被@rrousselGit提到的相同错误广泛覆盖(#51752)

在这种情况下,我建议参与该讨论; 这个特定的问题已经结束,但那个问题是开放的。 在那个问题上有一些关于提案的描述,但似乎没有一个做得很好。 那里还没有对“钩子”有特别清晰的描述,只是顺带提了一下。

我仍然不明白为什么当 React 和 Vue 清楚地记录了问题和建议的解决方案时,我们为什么要解释这个问题。

hooks 的 RFC包含来自许多聪明人的大约 1400 条评论、介绍视频、文档和文章。

我们可以不同意这些问题的解决方案。 但应该没有必要解释问题

这个问题在#51752 中有解释,不是吗? 这不是问题吗?

(至于为什么:因为将开发团队指向不同产品的 1400 条评论 RFC 并不是与该开发团队沟通的有效方式。如果感觉我很迟钝,我很抱歉。)

抱歉 ping 超线程。 只是想对那些一直关注这个的人说,我在https://github.com/flutter/flutter/issues/51752#issuecomment -665380355 中从 React 的角度留下了一些想法,并且很乐意回答更多问题是否有用。

我还想表达希望这个(和相关的线程)可以保持文明,而不是用诸如“它适用于 React”之类的论点向维护者施压。 React 和 Flutter 在编程模型上有显着差异,而 React Hooks 特别依赖于我们的一些细微差别。 因此,他们也有一些怪癖,许多人可能不会接受。

我已经改变了对钩子的看法。 我认为它们是一个很棒的模式,但我刚刚完成了一个 React Native 合约,并且钩子(在我看来)已经将开发系统非常碎片化,收益甚微。 Flutter 目前使用的模式与 React 类组件非常相似,并且围绕它构建了大量工具。 如果框架要切换到钩子作为主要的状态管理解决方案,它将破坏 Flutter 开发人员使用的所有现有工作和思维模式,收益甚微。

我认为有一个论点认为钩子是生产开发的优越模式,但我认为有更令人信服的论点(如 dartfmt 核心论点)随着时间的推移保持一致性比“更好”更好。

我还应该注意,在与新的 React 开发人员打交道时,我们经常会遇到障碍,钩子会产生意想不到的结果,并且必须先教他们使用类组件。 (一个很好的比较是新开发人员使用 for 循环比 map/reduce/filter/fold 等收集方法更容易使用的现象)。 Hooks 是一种高级模式,我们有时认为这是理所当然的。 令人沮丧的是,React 社区正在迅速淘汰文档和对类组件模式的支持,这使得为新开发人员提供这种教育或选择变得更加困难。

我在另一个问题 #51752 中提到过,也许我们应该努力创建一个更加 Flutter 特定版本的钩子,因为钩子本身似乎有一些缺点,例如useEffect(() => ..., [] )模式仅用于一次渲染。 @Hixie使用 Property 和 PropertyManager 模式制作了一个有趣的版本,它似乎做了类似于钩子的事情,但可能没有这些缺点。 我们应该更多地研究 hooks 的替代品,因为至少对于 Flutter 来说,感觉有些东西比 React 风格的 hooks 工作得更好,但仍然解决了同样的问题。

此页面是否有帮助?
0 / 5 - 0 等级