javascript 自定义获取钩子避免加载状态的无限循环

mlmc2os5  于 2023-06-04  发布在  Java
关注(0)|答案(1)|浏览(87)

我有一个项目,我创建了一个自定义钩子“useApi”,它管理对我的后端API的调用,以避免每次我需要调用API时重复代码。
这个useApi钩子返回一个数组,第一个参数是加载状态,第二个参数是调用API的函数。
默认情况下,加载状态设置为true,当fetch完成时,加载状态设置为false。
我的问题来了,在某些情况下,我需要调用事件(点击和表单提交)的API。在这种情况下,我会将加载状态默认设置为false,然后在获取之前将其设置为true,并在获取完成时将其设置回false。在下面我的自定义钩子的代码中,我在fetch之前注解了setLoading(true)行,否则会导致无限循环。
我想我有点明白为什么这个无限循环是因为加载状态更新。但是我找不到一个解决方案,如何实现这种“动态”加载状态

import { useState } from "react";

export default function useApi({method, url}) {

    const [loading, setLoading] = useState(true);

    const abortController = new AbortController();
    const fetchBaseUrl = import.meta.env.VITE_APP_URL + '/api/' + url;

    const methods = {
        get : function(data = {}) {
            // setLoading(true);
            const params = new URLSearchParams(data);
            const queryString = params.toString();
            const fetchUrl = fetchBaseUrl + (queryString ? "?"+queryString : "");
            
            return new Promise((resolve, reject) => {
                fetch(fetchUrl, {
                    signal : abortController.signal,
                    headers: {
                        "Content-Type": "application/json",
                        "Accept": "application/json",
                    },
                })
                .then(response => response.json())
                .then(data => {
                    setLoading(false);
                    if (!data) {
                        return reject(data);
                    }
                    resolve(data);
                })
                .catch(error => {
                    setLoading(false);
                    if (!abortController.signal.aborted) {
                        reject(error);
                    }
                });
            });
        },
        post :  function(data = {}) {
            // setLoading(true);
            const bodyData = {
                signal : abortController.signal,
                ...data
            };

            return new Promise((resolve, reject) => {
                fetch(fetchBaseUrl, {
                    method: "post",
                    headers: {
                        "Content-Type": "application/json",
                        "Accept": "application/json",
                    },
                    body: JSON.stringify(bodyData)
                })
                .then(response => response.json())
                .then(data => {
                    setLoading(false);
                    if (!data) {
                        return reject(data);
                    }
                    resolve(data);
                })
                .catch(error => {
                    setLoading(false);
                    if (!abortController.signal.aborted) {
                        reject(error);
                    }
                });
            });
        }
    }

    if ( !(method in methods) ) {
        throw new Error("Incorrect useApi() first parameter 'method'")
    }

    return [loading, methods[method]];
}

我尝试的是:

  • 使用useRef()钩子替换加载状态:const loading = useRef(false);和更新loading.current之前和之后的fetch,但这没有工作,在我的组件,我调用了自定义胡克,加载值接收不更新
  • 在具有'data'状态依赖项的useEffect中调用fetch函数(const [data, setData] = useState({})),并返回setData而不是fetch函数,也不起作用,setData在某些情况下会进行无限循环(在react-rooter加载器函数中,我有一个无限循环,但在en事件提交事件中没有)
laik7k3q

laik7k3q1#

在简化了我的自定义钩子之后,我的无限循环错误并不是来自我的setLoading()调用:

import { useState } from "react";

export default function useApi({method, url}) {

    const [loading, setLoading] = useState(false);

    const methods = {
        get: function (data = {}) {
            return new Promise((resolve, reject) => {
                setLoading(true);
                const params = new URLSearchParams(data);
                const queryString = params.toString();
                const fetchUrl = url + (queryString ? "?"+queryString : "");
                fetch(fetchUrl, {
                    method: "get",
                    headers: {
                        "Content-Type": "application/json",
                        "Accept": "application/json",
                    },
                })
                .then(response => response.json())
                .then(data => {
                    if( !data ){
                        setLoading(false);
                        return reject(data);
                    }
                    setLoading(false);
                    resolve(data);
                })
                .catch(error => {
                    setLoading(false);
                    console.error(error);
                });
            })
        },
        post :  function (data = {}) {
            return new Promise((resolve, reject) => {
                setLoading(true);
                fetch(url, {
                    method: "post",
                    headers: {
                        "Content-Type": "application/json",
                        "Accept": "application/json",
                    },
                    body: JSON.stringify(data)
                })
                .then(response => response.json())
                .then(data => {
                    if( !data ){
                        setLoading(false);
                        return reject(data);
                    }
                    setLoading(false);
                    resolve(data);
                })
                .catch(error => {
                    setLoading(false);
                    console.error(error);
                });
            })
        }
    }

    if ( !(method in methods) ) {
        throw new Error("Incorrect useApi() first parameter 'method'")
    }

    return [loading, methods[method]];
}

相关问题