我有一个useApi自定义钩子,它接受一个带有多个参数的端点(url(。当其中一个参数发生更改时,将渲染图形。问题是,当一个参数发生变化时,另一个参数也会发生变化,图形会被渲染两次。我该如何解决?感谢
const useApi = (endpoint, requestType, body) => {
const [data, setData] = useState({ fetchedData: [], isError: false, isFetchingData: false });
useEffect(() => {
requestApi();
}, [endpoint]);
const requestApi = async () => {
let response = {};
try {
setData({ ...data, isFetchingData: true });
console.log(endpoint);
switch (requestType) {
case 'GET':
return (response = await axios.get(endpoint));
case 'POST':
return (response = await axios.post(endpoint, body));
case 'DELETE':
return (response = await axios.delete(endpoint));
case 'UPDATE':
return (response = await axios.put(endpoint, body));
case 'PATCH':
return (response = await axios.patch(endpoint, body));
default:
return (response = await axios.get(endpoint));
}
} catch (e) {
console.error(e);
setData({ ...data, isError: true });
} finally {
if (response.data) {
setData({ ...data, isFetchingData: false, fetchedData: response.data.mainData });
}
}
};
return data;
};
有几个地方可以重构:
首先,您可以通过将useEffect
中的data
依赖项转换为以下内容来消除它:
setData(currentData => {
return { ...currentData, isFetchingData: true }
})
第二点也是最重要的一点是,您应该将requestApi
函数移动到useEffect
内部,或者用useCallback
函数包装它。
最后,如果有多个渲染,然后是另一个渲染,这是完全可以的。因为您依赖于useEffect
中的所有参数。
您可以做的一件事是通过返回useEffect中的函数来取消卸载期间的axios请求。
这是您代码的最终版本:
const useApi = (endpoint, requestType, body) => {
const [data, setData] = useState({
fetchedData: [],
isError: false,
isFetchingData: false
})
useEffect(() => {
let axiosSource = axios.CancelToken.source() // generate a source for axios
let didCancel = false // we can rely on this variable.
const requestApi = async () => {
let response = {}
try {
setData(data => {
return { ...data, isFetchingData: true }
})
console.log(endpoint)
const axiosOptions = { cancelToken: axiosSource.token }
switch (requestType) {
case 'GET':
return (response = await axios.get(endpoint, axiosOptions))
case 'POST':
return (response = await axios.post(endpoint, body, axiosOptions))
case 'DELETE':
return (response = await axios.delete(endpoint, axiosOptions))
case 'UPDATE':
return (response = await axios.put(endpoint, body, axiosOptions))
case 'PATCH':
return (response = await axios.patch(endpoint, body, axiosOptions))
default:
return (response = await axios.get(endpoint, axiosOptions))
}
} catch (e) {
console.error(e)
if (!didCancel) {
setData(data => {
return { ...data, isError: true }
})
}
} finally {
// do not update the data if the request is cancelled
if (response.data && !didCancel) {
setData(data => {
return {
...data,
isFetchingData: false,
fetchedData: response.data.mainData
}
})
}
}
}
requestApi()
// Here we are saying to axios cancel all current ongoing requests
// since this is the cleanup time.
return () => {
didCancel = true
axiosSource.cancel()
}
}, [body, endpoint, requestType])
return data
}
我没有测试代码。但它应该起作用。请试着告诉我发生了什么事。