[译] 理解 React Render Props 和 HOC

React 中 Render Props 和高阶组件的详细介绍

reactjs.orgjavascript

若是你最近有在作 React 开发,你确定有遇到像 HOCs 和 Render Props 这样的术语。在本文中,咱们将深刻探讨这两种模式,以便了解咱们为何须要它们,以及如何正确地使用它们来构建更好的 React 应用。css

为何咱们须要这些模式?

React 提供了一种简单的代码复用方式,即组件。组件封装了不少东西,包括内容、样式和业务逻辑。理想状况下,在单个组件中,咱们能够将 html、css 和 js 结合起来,全部的这些是为了一个目的,单一职责html

提示:使用 Bit (Github),你能够组织和分享可复用的组件,这些组件能够从不一样的项目和应用中被发现,分享和开发。这比重写组件或者维护一个大型库要快得多。试试看 :)前端

例子

假设咱们正在开发一个电子商务应用程序。它与其余电子商务应用程序同样,向用户显示全部可购买产品,而且用户能够将任何产品添加到购物车。咱们将从 API 获取产品数据,并将产品目录显示为卡片列表。java

在这种状况下,React 组件能够像这样实现:react

代码连接android

对于咱们的管理员,有一个管理门户,他们能够在其中添加或删除产品。在此门户中,咱们从同一 API 获取产品数据,并以表格形式显示产品目录。ios

这个 React 组件能够像这样实现:git

代码连接github

有一件事很明显,这两个组件都实现了产品的数据获取逻辑。

继续深刻,如下这些状况也可能出现:

  • 咱们必须使用产品数据并以不一样的方式显示它。
  • 从不一样的 API 中获取产品数据(在用户的购物车页面中颇有用),但数据的显示和咱们在 ProductList 中的作法相似。
  • 咱们必须从 localStorage 访问它,而不是从 API 获取数据。
  • 在产品目录表格中,须要使用具备不一样操做的按钮而不是删除按钮。

若是咱们为这些每一点都写个不一样的组件,那么咱们将要复制大量的代码。

获取数据和显示数据是两个独立的关注点。正如前面所说的,若是一个组件有一个责任会更好。

让咱们重构第一个组件。它将接受产品数据为属性,并像以前同样把产品目录渲染成卡片列表。因为咱们不须要组件状态和生命周期方法,咱们把它转换成函数式组件。

它如今看起来是这样的:

ProductList.js (代码连接)

就像 ProductListProductTable 会是一个函数组件,它接收产品数据为属性,并把数据渲染到表的行中去。

如今让咱们建立一个名为 ProductsData 的组件。它从 API 获取产品数据。数据的获取和状态的更新将和原先的 ProductList 组件同样。可是咱们应该在这个组件的 render 方法中放入什么呢?

ProductData.js (代码连接)

若是咱们只是简单的放入 ProductList 组件,那么咱们就不能复用这个组件于 ProductTable。无论怎样,若是这个组件能够询问要渲染什么,那个问题就会获得解决。在一个地方,咱们将告诉它要渲染 ProductList 组件,而在管理门户中,咱们告诉它要渲染 ProductTable 组件。

这就是 Render Props 和 HOCs 发挥做用的地方。它们只是一类方法,即对于一个组件,会询问应该渲染什么内容。这进一步推进了代码的复用。

如今咱们知道了为何须要它们,让咱们来看看如何使用它们。

Render Props

在概念层面理解 Render Props 很是简单。让咱们忘掉 React 一会,而后看看原生 JavaScript 下的事情。

咱们有一个计算两个数字之和的函数。起初咱们只想要把结果记录到控制台。因此,咱们这样设计函数:

可是,咱们很快发现 sum 函数很是有用,咱们须要在其余地方使用到它。所以,咱们但愿 sum 函数只提供结果,而不是将其记录到控制台,并让调用者决定如何使用结果。

它能够这么作:

代码连接

咱们传给 sum 函数一个 fn 回调函数做为参数。而后 sum 函数计算结果并把结果做为参数调用 fn。经过这种方式,回调函数能够得到结果,而且能够自由地对结果进行任何操做。

这就是 Render Props 的本质。咱们将经过使用这个模式来更清晰地认识它,因此让咱们马上把它用到咱们如今面临的问题中去吧。

在这不是计算两个数字之和的函数,而是获取产品数据的组件 ProductsData。如今能够经过属性传递给 ProductsData 组件一个函数。而后 ProductsData 组件将获取产品数据,并将这些数据提供给以属性方式传递进来的函数。传递进来的函数如今能够对产品数据作任何它想作的事情。

在 React 中,它能够像这样实现:

代码连接

就像 fn 参数,咱们有一个 render 属性,它将做为一个函数被传递。而后 ProductData 组件把产品数据做为参数调用这个函数。

所以咱们能够以这种方式使用 ProductData 组件。

代码连接

正如咱们所看到的 Render Props 是一种至关通用的模式。大部分事情均可以很是直接地完成。但这也是咱们搬起石头砸本身的脚的缘由:

避免嵌套的一种简单方法是把组件拆解成更小的组件,并将这些组件保存在单独的文件中。另外一种方法是编写更多的组件并组合它们,而不是在 Render Props 中使用长函数。

接下来,咱们将看下另外一种流行的模式,它被称为 HOC。

高阶组件(HOC)

在这个模式中,咱们定义了一个函数,该函数接受一个组件做为参数,而后返回相同的组件,可是添加了一些功能。

若是这听起来很熟悉,那是由于它相似于 Mobx 中普遍使用的装饰器模式。像 Python 这样的许多语言都内置了装饰器,JavaScript也很快就会支持装饰器。HOCs 很像装饰器。

比起用文字解释,用经过代码来理解 HOCs 会容易不少。因此让咱们先来看代码。

代码连接

正如咱们所看到的,数据获取和状态更新逻辑就和咱们在 Render Props 所作的同样。惟一的变化就是组件类是位于函数内部。该函数接受一个组件为参数,而后在内部的 render 方法中渲染这个组件,可是会添加额外的属性。对于名称如此复杂的模式,实现起来至关简单,对吧?

代码连接

如今咱们已经了解了为何咱们须要 Render Props,HOCs 以及咱们如何实现它们。

还有一个问题:如何在 Render Props 和 HOCs 中进行选择?关于这个话题的文章已经有不少了,因此我如今不讨论这个话题。也许在个人下一篇文章中 :)

何时不要使用 Render Props — Kent C. Dodds

HOCs vs Render Props — Richard Kotze

结论

在本文中,咱们了解了为何须要这些模式,每一个模式的本质和如何利用这些模式来构建高度可复用的组件。以上就是所有内容,但愿你喜欢,请随意评论和提问。我很乐意交流 👏

2018 年 10 月更新:React hooks 已经在 alpha 版本中中发布。它们将消除编写类组件、HOCs 和 Render Props 的痛苦。我很快就会写一篇来解释,关注个人 TwitterMedium 或者订阅 个人时事通信 来获取最新消息。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索