React的useRequest()钩子,再次审视run参数传递的问题
发布于 作者:苏南大叔 来源:程序如此灵动~
useRequest()的官方文档里面,给出的demo,都没有对网络请求使用参数的问题,进行很好的处理和说明。唯一相关的线索就是有个参数叫做defaultParams。这里的文档对新手还是挺不友好的。苏南大叔就useRequest()执行的请求,使用参数的问题,做个简单的总结。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0,create-react-app@5.0.1,react-router-dom@6.27.0,react@18.3.1。文章的原理,以前的文章里面都说过。这里就是一篇总结性的文章。
前文回顾
本文不涉及proxy的问题,也不涉及express接收参数的问题。和本文最相关的文章,有以下几篇:
- https://newsn.net/say/react-userequest.html
- https://newsn.net/say/react-proxy-2.html
- https://newsn.net/say/js-bind.html
总体上来说,如果对本文产生迷惑的话,就是语法的问题。集中表现在导出函数函数run身上,它代表了将要被执行的fetchData,并且没有传递任何参数。
const { run } = useRequest( fetchData );起点,fetchData
先说这个事情的宇宙原点fetchData,注意这里写法上就是个函数定义,并不是个带()的函数执行。如果对其再进行包装的话,可以是这样的:
fetchData() => fetchData()() => {return fetchData();},注意这里的return字样,非常重要。(...args) => fetchData(...args)
这里,其实主要考察的是匿名函数的写法,所以,不明白的可以参考下面的这篇文章:
原始状态,没实际使用的参数
如果这个时候,如果fetchData内的逻辑,不真正使用参数的话,上面的写法就全部合格没有问题。也可以在代码体中,使用run字样来激活上述代码中fetchData。例如:
const { run } = useRequest( fetchData );<button onClick={run}>请求</button>日常状态,使用参数逻辑
情况有变,fetchData需要接收并使用一个参数url,这个时候必须保证这个url参数逻辑。例如:
function fetchData(url) {
console.log(url);
if (url === undefined) {
// 首次刷新,并且没有设置defaultParams
alert("参数缺失");
return;
} else if (typeof url != "string") {
// 单独执行`run`,参数传递错误。
alert("参数类型错误");
return;
}
// run.bind({},url)
return fetch(url).then((response) => {
return response.json();
});
}这个情况下,参数的获取和执行,就非常重要了。
修改点一
可以凸显url参数的存在,或者不凸显。(并不重要,不修改也可以,或者改成其它也行)
const { data, run } = useRequest(fetchData);或者
const { data, run } = useRequest((url)=>fetchData(url));或者
const { data, run } = useRequest((...args)=>fetchData(...args));修改点二【重点】
如果还使用官方推荐的写法({run})的话,接收到的url是个[object object],所以是错误的调用。例如:
<button onClick={run}>请求</button>正确的写法,是写个.bind()的形式。(在react项目里面,很常见的)
<button onClick={run.bind({},"/api/url")}>正确写法请求</button>
<button onClick={()=>run("/api/url")}>正确写法请求2</button>这样写的话,就能正确接收到参数url。
修改点三【重点】
对于useRequest()这个钩子来说,它有个默认组件加载完就自动执行的选项(manual: false)。这种情况下,按照官方文档的写法,拿到的url就是个undefined。
解决方案是:再增加个新的选项defaultParams:[]。
defaultParams是个被动效果。在自动执行的时候,传递个默认的参数进去。和单独使用的run没有关系。run是个主动效果。在人为触发run的时候,默认情况下是没有明面上传递参数的。实际上潜藏的传递了个object作为第一个参数。然后误打误撞,称为了url参数。
苏南大叔这边理解着,defaultParams就是和...args的args是一样的效果,是个参数集合。
const { data, run } = useRequest( fetchData,{
defaultParams:['/api/default']
});const { data, run } = useRequest( (url)=>fetchData(url),{
defaultParams:['/api/default']
});测试代码
App.js:
import React from "react";
import { useRequest } from "ahooks";
function fetchData(url) {
console.log(url);
if (url === undefined) {
// 首次刷新,并且没有设置defaultParams
alert("参数缺失");
return;
} else if (typeof url != "string") {
// 单独执行`run`,参数传递错误。
alert("参数类型错误");
return;
}
// run.bind({},url)
return fetch(url).then((response) => {
return response.json();
});
}
function App() {
const { data, run, refresh } = useRequest(fetchData, {
defaultParams: ["/api/defaultParams"],
// refresh和这个参数无关,机器重复上一次请求,上一次错误这次还错误,上一次正确这次还正确。
});
return (
<div>
<h3>有参数的时候,需要更换run的写法,使用 .bind()</h3>
1、首次刷新自动执行的时候,需要使用defaultParams
<br />
<button onClick={run}>2、正确写法变错误,object</button>
<br />
<button onClick={run.bind({}, "/api/run")}>3、正确写法,bind</button>
<br />
<button onClick={() => run("/api/run2")}>4、正确写法2,匿名函数</button>
<br />
<button onClick={refresh}>5、refresh,无关对错</button>
<br />
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default App;
src/setupProxy.js:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/api", {
target: "http://localhost:3222",
changeOrigin: true,
pathRewrite: {
"^/api/": "",
},
})
);
};express.js:
const express = require("express");
const app = express();
var cors = require("cors");
app.use(cors());
app.all("*", (req, res) => {
ret = {
host: req.headers.host,
url: req.url,
time: Date.now().toString().slice(-6),
};
res.send(JSON.stringify(ret));
});
const server = app.listen(process.env.PORT || 3222, () => {
const port = server.address().port;
console.log("http://localhost:%d", port);
});useRequest的更多写法
还是useRequest()这个钩子的最基本的使用,核心代码:
const { data, run } = useRequest(fetchData);const { data, run } = useRequest(()=>fetchData());const { data, run } = useRequest(()=>{fetchData()}); //错误的写法,丢了`return`const { data, run } = useRequest(()=>{return fetchData()});const { data, run } = useRequest((url)=>fetchData(url));const fetchDataBefore = function(url){
return fetchData(url);
}
const { data, run } = useRequest(fetchDataBefore);run的更多写法
单纯的run,不能判断是否正确的写法。和是否有参数需求有关。
<button onClick={run}>运行</button><button onClick={run.bind({},"/link2")}>运行</button><button onClick={()=>run()}>运行</button><button onClick={()=>run("/api/run")}>运行</button>
题外话,refresh
在userequest官方文档里面,除了run(runasync)外,还有另外一个refresh导出函数。它和参数是否传递没有关系。因为它的主要作用就是重复上一条请求。那么,它无关对错,无关参数。
const { data, run, refresh } = useRequest(fetchData, {
defaultParams: ["/api/defaultParams"],
// refresh和这个参数无关,机器重复上一次请求,上一次错误这次还错误,上一次正确这次还正确。
});<button onClick={refresh}>refresh,无关对错</button>结语
更多react经验文章总结,参考苏南大叔的博客: