我们在使用 react 开发前端应用时,不可避免地要进行数据请求,而在数据请求的前后都要执行很多的处理,例如 当请求比较多时,每次都要重复这样的操作。这里我们可以利用 react 提供的 hook,自己来封装一个 umi 框架中已经有实现了一个 useRequest 方法,useRequest-umi-hook,他这里实现的功能很多,我们只实现一个基本的功能,其他更多的功能,您可以自己拓展。 在 useRequest 中,我们基于 axios 来封装,不过这里我们不会对外暴露太多的配置,首先我们来明确 useRequest 的输入和输出。 要输入的数据: 要返回的数据: 我们在确定输入和输出的格式后,就可以具体来实现了。 首先定义好输入和输出的数据: 然后就可以添加数据请求了: 在我们不考虑第三个配置 config 的情况下,这个 我们在请求接口过程中,可能接口还没没有返回到数据,组件就已经被销毁了,因此我们还要添加上取消请求的操作,避免操作已经不存在的组件。关于 axios 如何取消请求,您可以查看之前写的一篇文章:axios 源码系列之如何取消请求。 然后,接着我们就要考虑把配置加上了,在上面的调用中,只要调用了 useRequest,就会马上触发。但若在符合某种情况下才触发怎办呢(例如用户点击后才产生接口请求)? 这里我们就需要再加上一个配置。 这里我们就要考虑 useRequest 中触发 reqeust 请求的条件了: 使用方式: 当然,实现手动触发的方式有很多,我在这里是用一个 我们在这里实现的
useRequest
。1. 搭建一个基本结构 #
2. 具体的实现 #
2.1 不带 config 配置的 #
const useRequest = (url, data, config) => {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
return {
loading,
result,
error,
};
};
export default useRequest;
const useRequest = (url, data, config) => {
const [loading, setLoading] = useState(true);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const request = async () => {
setLoading(true);
try {
const result = await axios({
url,
params: data,
method: 'get',
});
if (result && result.status >= 200 && result.status <= 304) {
setResult(result.data);
} else {
setError(new Error('get data error in useRequest'));
}
} catch (reason) {
setError(reason);
}
setLoading(false);
};
useEffect(() => {
request();
}, []);
return {
loading,
result,
error,
};
};
useRequest
就已经可以使用了。const App = () => {
const { loading, result, error } = useRequest(url);
return (
<div>
<p>loading: {loading}p>
<p>{JSON.stringify(result)}p>
div>
);
};
2.2 添加取消请求 #
const request = useCallback(() => {
setLoading(true);
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios({
url,
params: data,
method: 'get',
cancelToken: source.token, // 将token注入到请求中
})
.then((result) => {
setResult(result.data);
setLoading(false);
})
.catch((thrown) => {
// 只有在非取消的请求时,才调用setError和setLoading
// 否则会报组件已被卸载还会调用方法的错误
if (!axios.isCancel(thrown)) {
setError(thrown);
setLoading(false);
}
});
return source;
}, [url, data]);
useEffect(() => {
const source = request();
return () => source.cancel('Operation canceled by the user.');
}, [request]);
2.3 带有 config 配置的 #
{
"manual": false, // 是否需要手动触发,若为false则立刻产生请求,若为true
"ready": false // 当manual为true时生效,为true时才产生请求
}
const useRequest = () => {
// 其他代码保持不变,并暂时忽略
useEffect(() => {
if (!config || !config.manual || (config.manual && config.ready)) {
const source = request();
return () => source.cancel('Operation canceled by the user.');
}
}, [config]);
};
const App = () => {
const [ready, setReady] = useState(false);
const { loading, result, error } = useRequest(url, null, { manual: true, ready });
return (
<div>
<p>loading: {loading}p>
<p>{JSON.stringify(result)}p>
<button onClick={() => setReady(true)}>产生请求button>
div>
);
};
config.ready
来触发,在 umi 框架中,是对外返回了一个 run 函数,然后执行 run 函数再来触发这个 useRequest 的执行。const useRequest = () => {
// 其他代码保持不变,并暂时忽略
const [ready, setReady] = useState(false);
const run = (r: boolean) => {
setReady(r);
};
useEffect(() => {
if (!config || !config.manual || (config.manual && ready)) {
if (loading) {
return;
}
setLoading(true);
const source = request();
return () => {
setLoading(false);
setReady(false);
source.cancel('Operation canceled by the user.');
};
}
}, [config, ready]);
};
3. 总结 #
useRequest
,也只是实现几个基本功能!对于更大的业务来说,可能需求点更多,例如轮训,防抖等,这些功能就需要自己去添加啦!
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论