0

0

【翻译】使用自定义hooks对React组件进行重构

青灯夜游

青灯夜游

发布时间:2023-01-17 20:13:51

|

1411人浏览过

|

来源于掘金社区

转载

【翻译】使用自定义hooks对React组件进行重构

我时常会听到人们谈起React函数组件,提到函数组件会不可避免的变得体积更大,逻辑更复杂。毕竟,我们把组件写在了“一个函数”里,因此你不得不接受组件会膨胀导致这个函数会不断膨胀。React的组件中也有提到:

既然函数组件能做的事情越来越多,那么你的代码库中的函数组件总体上看会越来越长。【相关推荐:Redis视频教程、编程视频】

其中也提到我们应该:

尽量避免过早地添加抽象

如果你使用CodeScene,你可能会注意到它会在你的函数太长或者太复杂的时候对你提示警告。如果按照我们之前所说的,我们可能会考虑是不是应该将CodeScene相关警告配置的更加宽泛一些。当然这是完全可以做到的,但是我觉得我们不应该这么做,我们也不应该拒绝在代码中添加许多的抽象,我们可以从中获取到很多的好处,并且大多数时候我们花费的成本并不高。我们可以继续将我们的代码健康度保持的非常好!

处理复杂性

我们应该要意识到,虽然函数组件被写在“一个函数”里,但是这个函数仍然可以像别的函数一样,可以由许多其他函数组成的。像useStateuseEffect ,抑或是别的hooks,子组件它们本身也是个函数。因此我们自然可以利用相同的思路来处理函数组件的复杂性问题:通过建立一个新函数,来把即符合公共模式又复杂的代码封装起来

比较常见的处理复杂组件的方式是把它分解成多个子组件。但是这么做可能会让人觉得不自然或是很难准确的去描述这些子组件。这时候我们就可以借助梳理组件的钩子函数的逻辑来发现新的抽象点。

每当我们在组件内看到由useStateuseEffect 或是其他内置钩子函数组成的长长的列表时,我们就应该去考虑是否可以将它们提取到一个自定义hook中去。自定义hook函数是一种可以在其内部使用其他钩子函数的函数,并且创建一个自定义钩子函数也很简单。

如下所示的组件相当于一个看板,用一个列表展示一个用户仓库的数据(想像成和github类似的)。这个组件并不算是个复杂组件,但是它是展示如何应用自定义hook的一个不错的例子。

function Dashboard() {
  const [repos, setRepos] = useState([]);
  const [isLoadingRepos, setIsLoadingRepos] = useState(true);
  const [repoError, setRepoError] = useState(null);

  useEffect(() => {
    fetchRepos()
      .then((p) => setRepos(p))
      .catch((err) => setRepoError(err))
      .finally(() => setIsLoadingRepos(false));
  }, []);

  return (
    
{isLoadingRepos && } {repoError && {repoError}} {repos.map((r) => ( ))}
); }

我们要把钩子逻辑提取到一个自定义hook中,我们只需要把这些代码复制到一个以use 开头的函数中(在这里我们将其命名为useRepos):

/**
 * 请求所有仓库用户列表的hook函数
 */
export function useRepos() {
  const [repos, setRepos] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchRepos()
      .then((p) => setRepos(p))
      .catch((err) => setError(err))
      .finally(() => setIsLoading(false));
  }, []);

  return [repos, isLoading, error] as const;
}

必须用use 开头的原因是linter 插件可以检测到你当前创建的是个钩子函数而不是普通函数,这样插件就可以检查你的钩子函数是否符合正确的自定义钩子的相关规则

相比提炼之前,提炼后出现的新东西只有返回语句as const 。这里的类型提示只是为了确保类型推断是正确的:一个包含3个元素的数组,类型分别是Repo[], boolean, string | null 。当然,你可以从钩子函数返回任何你希望返回的东西。

Detect GPT
Detect GPT

一个Chrome插件,检测您浏览的页面是否包含人工智能生成的内容

下载

译者注:这里添加as const 在ts类型推断的区别主要体现在数字元素的个数。不添加as const ,推断的类型为(string | boolean | Repo[] | null)[],添加后的类型推断为readonly [Repo[], boolean, string | null]

将自定义钩子useRepos 应用在我们的组件中,代码变成了:

function Dashboard() {
  const [repos, isLoadingRepos, repoError] = useRepos();

  return (
    
{isLoadingRepos && } {repoError && {repoError}} {repos.map((i) => ( ))}
); }

可以发现,我们现在在组件内部无法调用任何的setter 函数,即无法改变状态。在这个组件我们已经不需要包含修改状态的逻辑,这些逻辑都包含在了useRepos 钩子函数中。当然如果你确实需要它们,你也可以在钩子函数的返回语句中将其暴露出来。

这么做有什么好处呢?React的文档中有提到:

通过提取自定义钩子函数,可以实现组件逻辑的复用

我们可以简单的想象一下,如果这个应用中的别的组件也需要展示仓库中的用户列表,那么这个组件需要做的就只有导入useRepos 钩子函数。如果钩子更新了,可能使用某种形式的缓存,或者通过轮询或更复杂的方法进行持续更新,那么引用了这个钩子的所有组件都将受益。

当然,提取自定义钩子除了可以方便复用外,还有别的好处。在我们的例子中,所有的useStateuseEffect 都是为了实现同一个功能——就是获取库用户列表,我们把这个看作一个原子功能,那么在一个组件中,包含很多个这样的原子功能也是很常见的。如果我们把这些原子功能的代码都分别提取到不同的自定义钩子函数中,就更容易发现哪些状态在我们修改代码逻辑时要保持同步更新,不容易出现遗漏的情况。除此之外,这么做的好处还有:

  • 越短小的函数越容易看懂
  • 为原子功能命名的能力(如useRepo)
  • 更自然的提供文档说明(每个自定义钩子函数的功能更加内聚单一,这种函数也很容易去写注释)

最后

我们已经了解到React的钩子函数并没有多么神秘,也和其他函数一样很容易就可以创建。我们可以创建自己的领域特定的钩子,进而在整个应用程序中重用。也可以在各种博客或“钩子库”中找到很多预先编写好的通用钩子。这些钩子可以想useStateuseEffect 一样很方便的在我们的项目中应用。Dan Abramov的useInterval钩子就是一个例子,例如你有一个类似于useRepos 的钩子,但是你需要可以轮询更新?那你就可以尝试在你的钩子中使用useInterval

英文原文地址:https://codescene.com/engineering-blog/refactoring-components-in-react-with-custom-hooks

【推荐学习:javascript视频教程

相关专题

更多
云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

0

2026.01.20

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

20

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

62

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

160

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 9万人学习

CSS3 教程
CSS3 教程

共18课时 | 4.7万人学习

Vue 教程
Vue 教程

共42课时 | 6.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号