本文是深入浅出 ahooks 源码系列文章的第八篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。

本篇文章算是该系列的一个彩蛋篇,记录一下第一次给开源项目提 PR 的过程(之前好像也有过,不过那个非常小的一个改动),希望能够帮助更多的人参与到开源项目中来。

起因

在写了几篇关于 ahooks 的文章之后,收到了官方同学的私信。

这让我受宠若惊的同时也有点小兴奋和惶恐。

兴奋是,之前感觉参与开源是一件遥不可及的事情,现在似乎我也能够去做了。当然也有私心,假如我的简历上有给开源项目做贡献的经历,那岂不是一个不错的加分项?

惶恐的是,我之前没有参与过开源项目,担心自己不能做好这件事。

根据大佬的建议,我决定先从一些 issue 入手,也就是帮忙解决一下 issue。

明确问题OR需求

于是我抱着试试看的态度,看了一下官方的 issue,看到这么一条。issue 详情

刚好我之前对 useRequest 源码做过一些分析——如何使用插件化机制优雅的封装你的请求。于是我决定 fix 一下这个 issue。

这个 issue 的需求很简单,就是希望轮询失败后,能够支持最大的轮询次数,假如失败的次数大于这个值,则停止轮询。

编码前准备

首先,从 ahooks 官方 GitHub 中 folk 一份。这个操作我之前已经做了。

第二步,基于 master 切换一个功能分支。如下:

  1. git checkout -b fix/pollingSupportRetryCount

最后就是环境的一些初始化操作,不同的仓库不同,ahooks 如下:

  1. yarn run init
  2. yarn start

功能实现

我们先来看下现在 useRequest 的轮询的实现,其原理主要是在一个请求结束的时候(不管成功与失败),通过 setTimeout 进行重新请求,达到轮询的效果。

  1. onFinally: () => {
  2. // 省略部分代码...
  3. // 通过 setTimeout 进行轮询
  4. timerRef.current = setTimeout(() => {
  5. fetchInstance.refresh();
  6. }, pollingInterval);
  7. },

我的想法是,定义一个 options 参数,pollingErrorRetryCount,默认为 -1,代表没有限制。

另外定义一个变量,记录当前重试的次数:

  1. const countRef = useRef<number>(0);

当开发者设置了 pollingErrorRetryCount,并且重试的数量大于该值,我们就直接返回,不执行轮询的逻辑。

当成功或者失败的时候,更新当前重试的次数:

  1. onError: () => {
  2. countRef.current += 1;
  3. },
  4. onSuccess: () => {
  5. countRef.current = 0;
  6. },

然后在请求结束的时候,判断重试的次数有没有达到了开发设置的次数,假如没有则执行重试操作。有则重置重试的次数,停止轮询。

  1. onFinally: () => {
  2. if (
  3. pollingErrorRetryCount === -1 ||
  4. // When an error occurs, the request is not repeated after pollingErrorRetryCount retries
  5. (pollingErrorRetryCount !== -1 && countRef.current <= pollingErrorRetryCount)
  6. ) {
  7. // 忽略部分代码
  8. timerRef.current = setTimeout(() => {
  9. fetchInstance.refresh();
  10. }, pollingInterval);
  11. } else {
  12. countRef.current = 0;
  13. }
  14. },

测试用例

上述整体的改造并不困难,但是我在写测试用例的时候,就开始踩坑了,因为我很少书写前端的测试用例,还是针对于 hooks 的测试用例。这里是我耗时最多的地方。

最终用例如下:

  1. // 省略部分代码...
  2. // if request error and set pollingErrorRetryCount
  3. // and the number of consecutive failures exceeds pollingErrorRetryCount, polling stops
  4. let hook2;
  5. let errorCallback;
  6. act(() => {
  7. errorCallback = jest.fn();
  8. hook2 = setUp(() => request(0), {
  9. pollingErrorRetryCount: 3,
  10. pollingInterval: 100,
  11. pollingWhenHidden: true,
  12. onError: errorCallback,
  13. });
  14. });
  15. expect(hook2.result.current.loading).toEqual(true);
  16. expect(errorCallback).toHaveBeenCalledTimes(0);
  17. act(() => {
  18. jest.runAllTimers();
  19. });
  20. await hook2.waitForNextUpdate();
  21. expect(hook2.result.current.loading).toEqual(false);
  22. expect(errorCallback).toHaveBeenCalledTimes(1);
  23. act(() => {
  24. jest.runAllTimers();
  25. });
  26. await hook2.waitForNextUpdate();
  27. expect(errorCallback).toHaveBeenCalledTimes(2);
  28. act(() => {
  29. jest.runAllTimers();
  30. });
  31. await hook2.waitForNextUpdate();
  32. expect(errorCallback).toHaveBeenCalledTimes(3);
  33. act(() => {
  34. jest.runAllTimers();
  35. });
  36. await hook2.waitForNextUpdate();
  37. expect(errorCallback).toHaveBeenCalledTimes(4);
  38. act(() => {
  39. jest.runAllTimers();
  40. });
  41. expect(errorCallback).toHaveBeenCalledTimes(4);
  42. act(() => {
  43. hook2.result.current.run();
  44. });
  45. act(() => {
  46. jest.runAllTimers();
  47. });
  48. await hook2.waitForNextUpdate();
  49. expect(errorCallback).toHaveBeenCalledTimes(5);
  50. hook2.unmount();
  51. // 省略部分代码...

大致解释下该测试用例的逻辑,我设置了重试三次,错误之后,运行了三次,errorCallback 就会被调用了 4 次(包括错误那次)。在第五次执行的时候,就不会执行 errorCallback,也就还是 4 次。然后我们手动 run 一次请求,期待 errorCallback 应该执行 5 次。

这里踩了一个坑,就是第五次请求的时候,我之前是会写一个等待定时器执行的操作,但实际上这里它是不会执行定时器的,导致一直报错,在这里折腾了很久。后来删除了下面的代码才执行成功。

  1. act(() => {
  2. jest.runAllTimers();
  3. });
  4. - await hook2.waitForNextUpdate();
  5. expect(errorCallback).toHaveBeenCalledTimes(4);

文档以及 Demo 补充

毕竟加了一个新的 API 参数,需要在文档中注明,而且中英文文档都需要补充,还加上了一个 Demo 示例。

提 PR

上述都完成之后,就可以提交你的代码了,提交完,去到在你 folk 过来的项目中,可以看到这个。

我们需要点击图中框起来的「Compare & pull request 」,之后就会出现如下图

默认会帮我们选好分支的,我们只需要完善其中的信息,还有我们之前提交的 message 也可以修改。最好可以用英文来解释,本次提交的内容。

最后点击提交之后就好了。

还有一个提 PR 的入口,如下所示:

最后等待官方 CR 就可以了(上面的实现其实部分是 CR 后改的)。目前该 PR 已经被合入到 master。

总结思考

给开源项目提 PR 操作过程不是一件很复杂的事情,重点在于需求的修改。往往需要考虑到多种边界场景,这个时候,我们就需要前端的单元测试来帮助我们覆盖全面的场景。

另外,对于一些还没有参与开源项目经验的同学来讲,我觉得类似 ahooks 这种工具库是一个不错的选择:

  • 它的模块划分更加清晰,你改了一个模块的功能,影响面可以更好的预估。对新人比较友好。
  • 逻辑相对简单,其实你会发现很多代码说不定在你们的业务项目中的 utils/hooks 文件夹中就有。
  • 社区比较活跃,维护者能够较快的响应。

希望对大家有所帮助。

记录第一次给开源项目提 PR的更多相关文章

  1. 怎么给开源项目提PR?

    1. fork 你要的项目 2. 下载到本地 相关步骤如下 在你需要的文件夹下面,右键 git bash 命令,打开 git 命令框 执行如下指令可将项目代码下载到当前目录 ~~~ git clone ...

  2. 记录第一次在egret项目中使用Puremvc

    这几天跟着另一个前端在做一个小游戏,使用的是egret引擎和puremvc框架,这对于我来说还是个比较大的突破吧,特此记录下. 因为在此项目中真是的用到了mvc及面向对象编程,值得学习 记录第一次在e ...

  3. 如何参与开源项目 - 细说 GitHub 上的 PR 全过程

    目录 一.概述 二.为什么要参与开源项目 三.为什么我想介绍如何 PR 四.我想参与开源项目,怎么开始? 4.1.寻找一个合适的开源项目 4.2.寻找贡献点 五.我要提交 PR,怎么上手? 5.1.第 ...

  4. 如何从零开始参与 Apache 顶级开源项目?| 墙裂推荐

    ​ 写在开头 从 2021 开始,有一个很有意思的说法经常在各大技术媒体或开源论坛中出现,「开源正在吞噬一切」.不论是否言过其实,从一个行业从业者的切身感知来看,开源确实从少数极客的小众文化成为主流的 ...

  5. 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密

    你真的了解字典(Dictionary)吗?   从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...

  6. 编译CM13源码添加来去电归属地 SudaMod开源项目,查看commit提交记录

    这个问题纠结了很多时间,感谢苏打先森@Sudamod的开源项目. 大家知道CM13是没有来去点归属地的,就算有那也是google,对于中国人不适用,所以这里把方法贡献出来. 1.与通话有关的app D ...

  7. 开源项目Material Calendar View 学习记录 (一)

    开源项目Material Calendar View 学习记录 Github: https://github.com/prolificinteractive/material-calendarview ...

  8. java开源项目之IQQ学习记录之项目环境搭建与启动

    本文链接地址:http://blog.csdn.net/sushengmiyan/article/details/18779727 作者:sushengmiyan 现在就码字说说今天晚上搞定的一个项目 ...

  9. 利用cocoapods管理开源项目,支持 pod install安装整个流程记录(github公有库)

    利用cocoapods管理开源项目,支持 pod install安装整个流程记录(github公有库),完成预期的任务,大致有下面几步: 1.代码提交到github平台 2.创建.podspec 3. ...

随机推荐

  1. 高通(QCOM)sensor bring up

    高通7150平台 1.添加驱动文件 2.添加编译 3.配置json文件 4.高通默认配置 5.部分sensor外挂电源 6.遇到的问题 1.添加驱动文件 路径:adsp_proc/ssc/sensor ...

  2. YAML在线验证

    推荐一个网站:YAML在线验证https://www.bejson.com/validators/yaml_editor/

  3. Colab教程(超级详细版)及Colab Pro/Colab Pro+使用评测

    在下半年选修了机器学习的关键课程Machine learning and deep learning,但由于Macbook Pro显卡不支持cuda,因此无法使用GPU来训练网络.教授推荐使用Goog ...

  4. Canal实现MySQL协议

    目录 代码流程 执行dump前 执行dump 在学习Canal的时候很好奇Canal是如何模拟成MySql Slave来接收数据的 MySql Slave会向主库发送dump协议来接收bin-log数 ...

  5. 论文阅读 Exploring Temporal Information for Dynamic Network Embedding

    10 Exploring Temporal Information for Dynamic Network Embedding 5 link:https://scholar.google.com.sg ...

  6. dubbox、zookeeper BUG记录

    主要错误信息: dubbo:com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method... Caused by: com.alib ...

  7. 在.NET 6.0上使用Kestrel配置和自定义HTTPS

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本章是<定制ASP NET 6.0框架系列文章>的第四篇.在本章,我们 ...

  8. 浪姐打分看不够?用几行Python代码模拟评委打分

    大家好鸭~我是小熊猫比赛大家都看过吧,每次是不是都对比赛成绩充满期待.特别是浪姐的打分看的简直欲罢不能- 今天就用Python来模拟评委打分,这个案例很短也很简单,很适合新手跟小白练习. 在某次十佳歌 ...

  9. 事务@Transactional注解的属性

    事务的传播行为 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行.事务的传播行为可以由传播属性指定.Sprin ...

  10. Map接口总结(如何使用默认方法)

    Map接口总结(如何使用默认方法) Map的基本使用 默认方法的问题,有什么坑 常用的默认方法应用场景 基本操作 get put(区别:Collection接口中添加为set) putAll remo ...