本文是深入浅出 ahooks 源码系列文章的第八篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。
本篇文章算是该系列的一个彩蛋篇,记录一下第一次给开源项目提 PR 的过程(之前好像也有过,不过那个非常小的一个改动),希望能够帮助更多的人参与到开源项目中来。
在写了几篇关于 ahooks 的文章之后,收到了官方同学的私信。
这让我受宠若惊的同时也有点小兴奋和惶恐。
兴奋是,之前感觉参与开源是一件遥不可及的事情,现在似乎我也能够去做了。当然也有私心,假如我的简历上有给开源项目做贡献的经历,那岂不是一个不错的加分项?
惶恐的是,我之前没有参与过开源项目,担心自己不能做好这件事。
根据大佬的建议,我决定先从一些 issue 入手,也就是帮忙解决一下 issue。
于是我抱着试试看的态度,看了一下官方的 issue,看到这么一条。issue 详情。
刚好我之前对 useRequest 源码做过一些分析——如何使用插件化机制优雅的封装你的请求。于是我决定 fix 一下这个 issue。
这个 issue 的需求很简单,就是希望轮询失败后,能够支持最大的轮询次数,假如失败的次数大于这个值,则停止轮询。
首先,从 ahooks 官方 GitHub 中 folk 一份。这个操作我之前已经做了。
第二步,基于 master 切换一个功能分支。如下:
git checkout -b fix/pollingSupportRetryCount
最后就是环境的一些初始化操作,不同的仓库不同,ahooks 如下:
yarn run init
yarn start
我们先来看下现在 useRequest 的轮询的实现,其原理主要是在一个请求结束的时候(不管成功与失败),通过 setTimeout 进行重新请求,达到轮询的效果。
onFinally: () => {
// 省略部分代码...
// 通过 setTimeout 进行轮询
timerRef.current = setTimeout(() => {
fetchInstance.refresh();
}, pollingInterval);
},
我的想法是,定义一个 options 参数,pollingErrorRetryCount,默认为 -1,代表没有限制。
另外定义一个变量,记录当前重试的次数:
const countRef = useRef<number>(0);
当开发者设置了 pollingErrorRetryCount,并且重试的数量大于该值,我们就直接返回,不执行轮询的逻辑。
当成功或者失败的时候,更新当前重试的次数:
onError: () => {
countRef.current += 1;
},
onSuccess: () => {
countRef.current = 0;
},
然后在请求结束的时候,判断重试的次数有没有达到了开发设置的次数,假如没有则执行重试操作。有则重置重试的次数,停止轮询。
onFinally: () => {
if (
pollingErrorRetryCount === -1 ||
// When an error occurs, the request is not repeated after pollingErrorRetryCount retries
(pollingErrorRetryCount !== -1 && countRef.current <= pollingErrorRetryCount)
) {
// 忽略部分代码
timerRef.current = setTimeout(() => {
fetchInstance.refresh();
}, pollingInterval);
} else {
countRef.current = 0;
}
},
上述整体的改造并不困难,但是我在写测试用例的时候,就开始踩坑了,因为我很少书写前端的测试用例,还是针对于 hooks 的测试用例。这里是我耗时最多的地方。
最终用例如下:
// 省略部分代码...
// if request error and set pollingErrorRetryCount
// and the number of consecutive failures exceeds pollingErrorRetryCount, polling stops
let hook2;
let errorCallback;
act(() => {
errorCallback = jest.fn();
hook2 = setUp(() => request(0), {
pollingErrorRetryCount: 3,
pollingInterval: 100,
pollingWhenHidden: true,
onError: errorCallback,
});
});
expect(hook2.result.current.loading).toEqual(true);
expect(errorCallback).toHaveBeenCalledTimes(0);
act(() => {
jest.runAllTimers();
});
await hook2.waitForNextUpdate();
expect(hook2.result.current.loading).toEqual(false);
expect(errorCallback).toHaveBeenCalledTimes(1);
act(() => {
jest.runAllTimers();
});
await hook2.waitForNextUpdate();
expect(errorCallback).toHaveBeenCalledTimes(2);
act(() => {
jest.runAllTimers();
});
await hook2.waitForNextUpdate();
expect(errorCallback).toHaveBeenCalledTimes(3);
act(() => {
jest.runAllTimers();
});
await hook2.waitForNextUpdate();
expect(errorCallback).toHaveBeenCalledTimes(4);
act(() => {
jest.runAllTimers();
});
expect(errorCallback).toHaveBeenCalledTimes(4);
act(() => {
hook2.result.current.run();
});
act(() => {
jest.runAllTimers();
});
await hook2.waitForNextUpdate();
expect(errorCallback).toHaveBeenCalledTimes(5);
hook2.unmount();
// 省略部分代码...
大致解释下该测试用例的逻辑,我设置了重试三次,错误之后,运行了三次,errorCallback 就会被调用了 4 次(包括错误那次)。在第五次执行的时候,就不会执行 errorCallback,也就还是 4 次。然后我们手动 run 一次请求,期待 errorCallback 应该执行 5 次。
这里踩了一个坑,就是第五次请求的时候,我之前是会写一个等待定时器执行的操作,但实际上这里它是不会执行定时器的,导致一直报错,在这里折腾了很久。后来删除了下面的代码才执行成功。
act(() => {
jest.runAllTimers();
});
- await hook2.waitForNextUpdate();
expect(errorCallback).toHaveBeenCalledTimes(4);
毕竟加了一个新的 API 参数,需要在文档中注明,而且中英文文档都需要补充,还加上了一个 Demo 示例。
上述都完成之后,就可以提交你的代码了,提交完,去到在你 folk 过来的项目中,可以看到这个。
我们需要点击图中框起来的「Compare & pull request 」,之后就会出现如下图
默认会帮我们选好分支的,我们只需要完善其中的信息,还有我们之前提交的 message 也可以修改。最好可以用英文来解释,本次提交的内容。
最后点击提交之后就好了。
还有一个提 PR 的入口,如下所示:
最后等待官方 CR 就可以了(上面的实现其实部分是 CR 后改的)。目前该 PR 已经被合入到 master。
给开源项目提 PR 操作过程不是一件很复杂的事情,重点在于需求的修改。往往需要考虑到多种边界场景,这个时候,我们就需要前端的单元测试来帮助我们覆盖全面的场景。
另外,对于一些还没有参与开源项目经验的同学来讲,我觉得类似 ahooks 这种工具库是一个不错的选择:
希望对大家有所帮助。
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里