在 React 中编写自己的 useFetch Hook
我们的 useFetch 函数签名
在 Hook 中维护状态
当我们的 URL 或选项改变时运行效果
使用 Async Await 调用 Fetch
使用 Hook
结论
React Hooks 风靡一时,已经一年多了。让我们看看如何实现我们自己的useFetch
Hook,将获取请求逻辑从组件中抽象出来。
注意:这仅用于学术目的。您可以自行开发hook 并将其用于生产环境,但我强烈建议使用像use-http 这样useFetch
的成熟库来帮您完成繁重的工作!
如果您喜欢这篇文章,请给它一个💓、🦄或🔖,并考虑订阅📬我的免费每周开发者新闻通讯
我们的 useFetch 函数签名
为了确定useFetch
函数签名,我们应该考虑实际执行获取请求时可能需要从最终用户那里获取的信息。在这种情况下,我们会说我们需要资源url
,以及options
可能与请求相关的信息(例如,请求方法)。
function useFetch(initialUrl, initialOptions) {
// Hook here
}
在功能更齐全的解决方案中,我们可能会为用户提供一种中止请求的方法,但目前我们对这两个论点感到满意!
在 Hook 中维护状态
我们的钩子需要维护一些状态。我们至少需要维护url
和options
的状态(因为我们需要为用户提供访问setUrl
和 的方法setOptions
)。此外,我们还需要一些其他状态变量!
- 数据(从我们的请求返回的数据)
- 错误(请求失败时出现的任何错误)
- 加载(布尔值,指示我们是否正在主动获取)
让我们使用内置useState
钩子创建一堆有状态变量。此外,我们还希望让用户有机会做以下事情:
- 设置网址
- 设置选项
- 查看检索到的数据
- 查看任何错误
- 查看加载状态
因此,我们必须确保从我们的钩子返回这两个状态设置函数和三个数据!
import { useState } from 'React';
function useFetch(initialUrl, initialOptions) {
const [url, setUrl] = useState(initialUrl);
const [options, setOptions] = useState(initialOptions);
const [data, setData] = useState();
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
// Some magic happens here
return { data, error, loading, setUrl, setOptions };
}
重要的是,我们将我们的url
和options
默认为钩子首次调用时提供的initialUrl
和initialOptions
。此外,您可能会想,这些变量有很多不同的,您想将它们全部保存在同一个对象或几个对象中——这完全没问题!
当我们的 URL 或选项改变时运行效果
这部分非常重要!我们需要fetch
在每次url
或options
变量发生变化时执行一次请求。还有什么比内置useEffect
钩子更好的方法吗?
import { useState } from 'React';
function useFetch(initialUrl, initialOptions) {
const [url, setUrl] = useState(initialUrl);
const [options, setOptions] = useState(initialOptions);
const [data, setData] = useState();
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
useEffect(() => {
// Fetch here
}, [url, options]);
return { data, error, loading, setUrl, setOptions };
}
使用 Async Await 调用 Fetch
比起 Promise 语法,我更喜欢 async/await 语法,所以就用前者吧!当然,用then
、catch
、 和 也一样好finally
,比用 async/await 好用。
import { useState } from 'React';
function useFetch(initialUrl, initialOptions) {
const [url, setUrl] = useState(initialUrl);
const [options, setOptions] = useState(initialOptions);
const [data, setData] = useState();
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
setError(undefined);
async function fetchData() {
try {
const res = await fetch(url, options);
const json = await res.json();
setData(json);
} catch (e) {
setError(e);
}
setLoading(false);
}
fetchData();
}, [url, options]);
return { data, error, loading, setUrl, setOptions };
}
真是太多了!我们稍微分解一下。运行效果时,我们知道开始获取数据了。因此,我们将loading
变量设置为true
,并清除之前可能存在的任何错误。
在异步函数中,我们fetch
用一个try/catch
块包装了请求代码。我们希望将遇到的任何错误报告给用户,因此在catch
块中,我们setError
会记录所有报告的错误。
在我们的代码块中try
,我们执行了一个相当标准的fetch
请求。我们假设返回数据是json
因为我很懒,但如果我们要让它成为最通用的钩子,我们可能会给用户提供一种配置预期响应类型的方法。最后,假设一切成功,我们将data
返回的 JSON 值赋给变量!
使用 Hook
信不信由你,这就是创建自定义钩子的全部步骤!现在我们只需要将它引入示例应用程序,并希望它能够正常工作。
在下面的示例中,我有一个应用可以加载任何 GitHub 用户的基本 GitHub 个人资料数据。除了设置fetch
选项之外,这个应用几乎实现了我们为钩子设计的所有功能。我们可以看到,在加载 fetch 请求时,我们可以显示“正在加载”指示。当 fetch 完成时,我们会显示错误信息或结果的字符串化版本。
我们为用户提供了一种输入不同 GitHub 用户名来执行新请求的方式。提交后,我们会调用钩子setUrl
导出的函数useFetch
,执行 effect 并发起新的请求。很快就能拿到新数据了!
const makeUserUrl = user => `https://api.github.com/users/${user}`;
function App() {
const { data, error, loading, setUrl } = useFetch(makeUserUrl('nas5w'));
const [user, setUser] = useState('');
return (
<>
<label htmlFor="user">Find user:</label>
<br />
<form
onSubmit={e => {
e.preventDefault();
setUrl(makeUserUrl(user));
setUser('');
}}
>
<input
id="user"
value={user}
onChange={e => {
setUser(e.target.value);
}}
/>
<button>Find</button>
</form>
<p>{loading ? 'Loading...' : error?.message || JSON.stringify(data)}</p>
</>
);
}
请随意查看此处 codesandbox 上的useFetch
钩子和示例应用程序。
结论
编写自定义 React Hook 可能是一件很有趣的事情。一开始可能会有点棘手,但一旦掌握了窍门,就会发现它非常有趣,而且可以大大缩短组件代码,减少冗余。
如果您对这个钩子、React 或 JS 有任何疑问,请随时通过 Twitter 与我联系!
鏂囩珷鏉ユ簮锛�https://dev.to/nas5w/writing-your-own-usefetch-hook-in-react-2i3g