React Hook 依赖项 - 通用 Fetch Hook

2024-04-23

我已经遵循了许多关于如何设置自己的自定义通用的教程useFetch钩。 我想出的方法效果很好,但它违反了一些 Hook 规则。 大多数情况下,它不使用“正确”的依赖项集。

通用挂钩接受 url、选项和依赖项。 设置依赖关系,因为所有三个都会创建无限刷新循环,即使依赖关系没有改变.

// Infinite useEffect loop - happy dependencies
const UseRequest: <T>(url: string, options?: Partial<UseRequestOptions> | undefined, dependencies?: any[]) => UseRequestResponse<T>
 = <T>(url: string, options: Partial<UseRequestOptions> | undefined = undefined, dependencies: any[] = []): UseRequestResponse<T> => {
    const [data, setData] = useState<T | undefined>();
    const [loading, setLoading] = useState<boolean>(false);
    const [error, setError] = useState<UseRequestError | undefined>();

    useEffect(() => {
        let ignore = false;
        (async () => {
            try {
                setLoading(true);
                const response = await (options ? fetch(url) : fetch(url, options))
                    .then(res => res.json() as Promise<T>);
                if (!ignore) setData(response);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        })();
        return (() => { ignore = true; });
    }, [url, options, dependencies]);
    return { data, loading, error };
}

我发现,如果我省略依赖项中的选项(这是有道理的,因为我们不希望这个深层对象以我们应该监视的方式发生变化)并传播传入的依赖项,它就会按预期工作。 当然,这两个变化都打破了“Hooks 规则”。

// Working - mad dependencies
const UseRequest: <T>(url: string, options?: Partial<UseRequestOptions> | undefined, dependencies?: any[]) => UseRequestResponse<T>
 = <T>(url: string, options: Partial<UseRequestOptions> | undefined = undefined, dependencies: any[] = []): UseRequestResponse<T> => {
    const [data, setData] = useState<T | undefined>();
    const [loading, setLoading] = useState<boolean>(false);
    const [error, setError] = useState<UseRequestError | undefined>();

    useEffect(() => {
        let ignore = false;
        (async () => {
            try {
                setLoading(true);
                const response = await (options ? fetch(url) : fetch(url, options))
                    .then(res => res.json() as Promise<T>);
                if (!ignore) setData(response);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        })();
        return (() => { ignore = true; });
    }, [url, ...dependencies]);
    return { data, loading, error };
}

...然后我就用它

export const GetStuff: () => UseRequestResponse<Stuff[]> & { refresh: () => void } = () => {
    const { appToken } = GetAppToken();
    const [refreshIndex, setRefreshIndex] = useState(0);
    return {
        ...UseRequest<Stuff[]>('https://my-domain.api/v1/stuff', {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${appToken}`
            }
        }, [appToken, refreshIndex]),
        refresh: () => setRefreshIndex(refreshIndex + 1),
    };
};

请注意,工作状态和损坏状态之间的唯一变化是:

}, [url, options, dependencies]);

...to:

}, [url, ...dependencies]);

那么,我怎样才能重写它以遵循 Hooks 规则而不陷入无限刷新循环呢?

这是完整的代码useRequest使用定义的接口:

import React, { useState, useEffect } from 'react';

const UseRequest: <T>(url: string, options?: Partial<UseRequestOptions> | undefined, dependencies?: any[]) => UseRequestResponse<T>
 = <T>(url: string, options: Partial<UseRequestOptions> | undefined = undefined, dependencies: any[] = []): UseRequestResponse<T> => {
    const [data, setData] = useState<T | undefined>();
    const [loading, setLoading] = useState<boolean>(false);
    const [error, setError] = useState<UseRequestError | undefined>();

    useEffect(() => {
        let ignore = false;
        (async () => {
            try {
                setLoading(true);
                const response = await (options ? fetch(url) : fetch(url, options))
                    .then(res => res.json() as Promise<T>);
                if (!ignore) setData(response);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        })();
        return (() => { ignore = true; });
    }, [url, ...dependencies]);
    return { data, loading, error };
}

export default UseRequest;

export interface UseRequestOptions {
    method: string;
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
        [prop: string]: string;
    },
    redirect: string, // manual, *follow, error
    referrerPolicy: string, // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: string | { [prop: string]: any };
    [prop: string]: any;
};

export interface UseRequestError {
    message: string;
    error: any;
    code: string | number;
    [prop: string]: any;
}

export interface UseRequestResponse<T> {
    data: T | undefined;
    loading: boolean;
    error: Partial<UseRequestError> | undefined;
}

那是因为您在每次渲染时重新创建一个新数组。事实上,整个依赖关系没有任何意义,因为您从未在效果中使用它。

您同样可以依赖选项对象,它具有不断变化的标头。但由于该对象也会在每次渲染时重新创建,因此您必须首先记住它:

export const GetStuff: () => UseRequestResponse<Stuff[]> & { refresh: () => void } = () => {
    const { appToken } = GetAppToken();
    const [refreshIndex, setRefreshIndex] = useState(0);

    const options = useMemo(() => ({
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${appToken}`
        }
    }), [appToken, refreshIndex])

    return {
        ...UseRequest<Stuff[]>('https://my-domain.api/v1/stuff', options),
        refresh: () => setRefreshIndex(refreshIndex + 1),
    };
};

然后,您可以使用以下方法,而不是依赖刷新索引来触发刷新useRequest()hook 返回一个刷新函数,该函数在内部也会调用效果中的该函数(而不是将加载逻辑放入效果本身,它只是调用该函数)。这样您就可以更好地遵守规则,因为useMemo实际上从不依赖于刷新索引,因此它不应该位于依赖项中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React Hook 依赖项 - 通用 Fetch Hook 的相关文章

随机推荐

  • 删除 Windows 窗体中的标题栏

    如何删除窗口窗体顶部的蓝色边框 我不知道它的确切名称 您可以设置属性FormBorderStyle对于设计师中的任何一个人来说 或者在代码中 this FormBorderStyle System Windows Forms FormBor
  • 在 Windows 上通过 pip 使用 fastmath(gmp 或 mpir)构建 PyCrypto

    我通过 pip 在 Windows 上安装了 PyCrypto 但无法构建 Crypto PublicKey fastmath 因为找不到 GMP 我知道有一个二进制版本虚空 http www voidspace org uk python
  • 如何在调度代码时自动选择R中googlesheets4中的预授权帐户?

    我试图弄清楚自动允许 googlesheet4 包选择我的预授权帐户来下载特定谷歌表格的方法是什么 例如 我想每天运行以下一次 library googlesheets4 delta lt read sheet https docs goo
  • 找出这样一座塔中尽可能多的人

    首先我们看一下问题 马戏团正在设计一种塔式表演 由人们站在彼此的塔顶上组成 肩膀 出于实用和美观的原因 每个人都必须比他或她下面的人矮且轻 给定马戏团中每个人的身高和体重 编写一个方法来计算最大可能的人数 在这样的一座塔里 EXAMPLE
  • React.lazy() 与 Typescript

    我收到错误 Element 类型中缺少属性 default 但类型中需要属性 default 默认 组件类型 ts 2322 React lazy gt import i18n locales this props lang then o
  • 使用 corona sdk 验证电子邮件地址

    在我的项目中 有一个供用户填写详细信息的表单 其中有一个文本字段用于输入用户的电子邮件 ID 所以我需要在 corona 项目中验证该文本字段中的电子邮件 试试这个正则表达式 local email email protected cdn
  • ImportError:无法从“tensorflow.python.keras.engine”导入名称“network”

    尝试使用 anaconda 环境导入在 Tensorflow 2 3 0 上运行的 tf agents environments 时出现此错误 尝试重新安装tensorflow 仍然出现同样的错误 以管理员身份运行 jupyter 笔记本
  • Maven:经常从远程存储库下载元数据 xml 文件

    我正在使用 Maven 来处理 Java 项目 我认为只有在第一次编译时才需要互联网连接来从远程存储库下载所需的库 但每当我编译代码时 我都会收到几条下载消息 像这样的消息 Downloading http repo maven apach
  • CSS:将“float:right”元素移动到顶部(与列表的第一个元素对齐)

    我有一系列元素 最后一个元素有 css float left 我想将其显示在与第一个元素相同的高度 而不是显示在列表的底部 我无法更改 html 代码 因此它是列表中的最后一个 同时 我想将其保留在右侧 我怎样才能用CSS制作它 thank
  • JPA 枚举 ORDINAL 与 STRING

    可以使用以下任一方式在 JPA 中定义枚举 Enumerated EnumType ORDINAL or Enumerated EnumType STRING 我想知道这两个定义的优点和缺点是什么 我听说 ORDINAL 在 Eclipse
  • 为 AlertDialog 的进入和退出设置动画

    我必须滑入AlertDialog当它进入时 当它消失时 将其滑出 但它不是动画 那么如何让动画发挥作用呢 这是我所拥有的 public class SlideDialogFragment extends DialogFragment Ove
  • 将文件读入多维数组

    我想从文件中读取数字 n n 网格并将它们复制到多维数组中 一次一个 int 我有读取文件并将其打印出来的代码 但不知道如何获取每个 int 我认为我需要 splitstring 方法和空白分隔符 才能获取每个字符 但在那之后我不确定 我还
  • 创建子列表并从上一个列表中删除值

    我想在 Java 中创建一个子列表 并从上一个列表中删除子列表中的值 我的程序正确创建了子列表 但它没有从前一个列表中删除正确的值 My code for int i 0 i lt 4 i List sub new ArrayList pr
  • 在 django admin 中更改字段名称

    我正在自定义 django admin 我想更改字段的显示名称 我认为答案是here https docs djangoproject com en dev ref contrib admin 但我找不到它 感谢 Meta 类 我已经更改了
  • 同一解决方案中的 ASP.NET Core 5 MVC/Razor Pages 和 Web API 项目

    许多网站分为两部分 www example com 公众MVC https learn microsoft com en us aspnet core mvc overview view aspnetcore 5 0 剃刀页面 https
  • Spark Streaming:如何在 Python 中获取已处理文件的文件名

    我对 Spark 老实说也是 Python 有点菜鸟 所以如果我错过了一些明显的东西 请原谅我 我正在使用 Spark 和 Python 进行文件流处理 在我做的第一个示例中 Spark 正确地侦听给定目录并计算文件中单词的出现次数 因此我
  • ScrollViewer 和 TextBlock 换行

    我有以下布局 简化
  • 无法在 C++ 中使用宏定义类

    我想生成许多几乎没有什么区别的子类 所以我想使用宏来简化我的工作 宏定义如下 define DECLARE SUB CLASS sub class name base class name value1 class sub class na
  • 在哪里可以找到用于 EJB 测试的完整 Maven Cargo 插件示例?

    对于一些小型 JBoss 企业应用程序的测试 我想使用 JUnit 并且Maven 货物插件 http cargo codehaus org Maven2 plugin 我知道还有 JSFUnit 但首先我想仔细看看 Cargo 网上是否有
  • React Hook 依赖项 - 通用 Fetch Hook

    我已经遵循了许多关于如何设置自己的自定义通用的教程useFetch钩 我想出的方法效果很好 但它违反了一些 Hook 规则 大多数情况下 它不使用 正确 的依赖项集 通用挂钩接受 url 选项和依赖项 设置依赖关系 因为所有三个都会创建无限