[英]How can I stop a recursing function on click?
So I have a function that fetches from an API until it hits a 200 code.所以我有一个 function,它从 API 中获取,直到它达到 200 代码。
Something like this:像这样:
/* These functions are in a separate file */
const fetchFile = async () => {
try {
const resp = await fetch('someUrl');
return resp.data;
} catch (err) {
if(err.response.code === 404){
await sleep(10); //sleep is a custom fn that awaits for N seconds
return fetchFile();
}else{
return 'Error';
}
}
}
const fetchAll = async (setData, setError) => {
const data = await fetchFile();
if(data === 'Error') {
setError('Sorry, error ocurred');
return;
}
setData(data);
}
/* React component */
const [data, setData] = useState([]);
const [error, setError] = useState('');
<button onClick={fetchAll}>Start fetch</button>
And I need to have another button that let's the user stop the recursing function. I know a common way of doing this is having a flag and check the value every time I call the recursing function, but since this is React, how can I achieve this?我需要另一个按钮让用户停止递归 function。我知道这样做的一种常见方法是有一个标志并在每次调用递归 function 时检查值,但由于这是 React,我该如何实现这个? Should I have the functions inside the component file?我应该在组件文件中包含函数吗? Use a global window variable?使用全局 window 变量? Use localstorage?使用本地存储? What's the best way?最好的方法是什么?
You can do couple of ways, here is one the way using useRef您可以采用多种方式,这是使用 useRef 的一种方式
/* These functions are in a separate file */ /* 这些函数在一个单独的文件中 */
const fetchFile = async (cancellationRef) => {
try {
const resp = await fetch('someUrl');
return resp.data;
} catch (err) {
if(err.response.code === 404){
if(cancellationRef.current !==true){
await sleep(10); //sleep is a custom fn that awaits for N seconds
return fetchFile();
}
else{
return 'Request cancelled';
}
}else{
return 'Error';
}
}
}
const fetchAll = async (setData, setError,cancellationRef) => {
const data = await fetchFile(cancellationRef);
if(data === 'Error') {
setError('Sorry, error ocurred');
return;
}
setData(data);
}
/* React component */
const [data, setData] = useState([]);
const cancellationRef = useRef(false);
const [error, setError] = useState('');
<button onClick={fetchAll}>Start fetch</button>
<button onClick={()=>cancellationRef.current =true)}>Stop looping</button>
Here is a full example - I recommend encapsulating the fetching behavior in a hook like this.这是一个完整的示例——我建议将抓取行为封装在这样的钩子中。 An AbortController is used for cancelling any ongoing request. AbortController用于取消任何正在进行的请求。 This takes advantage of useEffect
and its cleanup function, which is described in the react docs :这利用了useEffect
及其清理 function,这在反应文档中有描述:
If your effect returns a function, React will run it when it is time to clean up:如果您的效果返回 function,React 将在需要清理时运行它:
/** Continuously makes requests until a non-404 response is received */
const fetchFile = async (url, signal) => {
try {
const resp = await fetch(url, { signal });
return resp.data;
} catch (err) {
if(err.response.code === 404){
await sleep(10);
return fetchFile(url, signal);
}
// there was an actual error - rethrow
throw err;
}
}
/** Hook for fetching data and cancelling the request */
const useDataFetcher = (isFetching) => {
// I advise using `null` as default values
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
if (isFetching) {
const controller = new AbortController();
fetchFile('someUrl', controller.signal)
.then(result => {
setData(result);
setError(null);
})
.catch(err => {
if (err.name === "AbortError") {
// request was aborted, reset state
setData(null);
setError(null);
} else {
setError(err);
}
});
// This cleanup is called every time the hook reruns (any time isFetching changes)
return () => {
if (!controller.signal.aborted) {
controller.abort();
}
}
}
}, [isFetching]);
return { data, error };
}
const MyComponent = () => {
const [isFetching, setIsFetching] = useState(false);
const { data, error } = useDataFetcher(isFetching);
const startFetch = useCallback(() => setIsFetching(true), []);
const cancelFetch = useCallback(() => setIsFetching(false), []);
useEffect(() => {
if (data || error) {
setIsFetching(false);
}
}, [data, error];
return isFetching
? <button onClick={cancelFetch}>Cancel</button>
: <button onClick={startFetch}>Start fetch</button>
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.