在自定义反应钩子中使用 axios
Posted
技术标签:
【中文标题】在自定义反应钩子中使用 axios【英文标题】:Using axios inside custom react hook 【发布时间】:2020-07-18 09:34:50 【问题描述】:我想创建一个带有拦截器的全局 axios 实例,该拦截器添加带有访问令牌的标头。 访问令牌存储在 cookie 中,但我不知道如何访问拦截器内部的 cookie,因为它不是反应组件。
我曾尝试在这样的自定义挂钩中创建 axios 实例:
import React, useEffect, useState from "react";
import useCookies from "react-cookie";
import axios from "axios";
function useApiClient()
const [cookies, setCookie] = useCookies(["token"]);
const [client, setClient] = useState(null);
useEffect(() =>
setClient(
axios
.create(
baseURL: "http://localhost:8080/api/",
responseType: "json",
)
.interceptors.request.use(
(config) =>
console.log("setting up");
if (cookies["token"] != null)
config.headers.authorization = cookies["token"];
,
(error) => Promise.reject(error)
)
);
, [client]);
return client;
export default useApiClient;
但是当我尝试使用以下方法调用它时:
const client = useApiClient();
function attemptLogin()
const credentials =
username: username,
password: password,
;
client
.post("/authenticate", JSON.stringify(credentials),
headers:
"Content-Type": "application/json",
,
).then(....)
我得到以下错误:
TypeError: client.post 不是函数
谁能帮忙?
【问题讨论】:
我注意到的一个问题是您需要在数组中像这样的客户端依赖项:[client]
在 useEffect 中。您还可以展示如何使用 client.post 吗?
嗨@SuleymanSah 比你的评论。我曾尝试在数组中添加依赖项,但没有运气。我已经编辑了我的问题以显示我如何使用客户端。
您是否考虑过 axios 配置的 withCredentials: true
选项?
【参考方案1】:
问题是您正在尝试使用react-cookie
,它提供了一个用于在组件中使用cookies 的api。在某些情况下可能没问题,但是对于需要检查每个请求的 cookie 的情况,它会强制将 axios
实例放在组件层次结构顶部的某个位置,并且也使得在组件之外使用实例变得更加困难(大多数您可能会将实例传递给外部函数)。
我的建议是在单独的模块中创建一个实例,并使用不同的工具来获取 cookie,例如 universal-cookie。
// apiClient.js
import axios from "axios";
import Cookies from "universal-cookie";
const cookies = new Cookies();
export const apiClient = axios
.create(
baseURL: "http://localhost:8080/api/",
responseType: "json"
)
.interceptors.request.use(
config =>
console.log("setting up");
const token = cookies.get("token");
if (token != null)
config.headers.authorization = token;
,
error => Promise.reject(error)
);
之后,您可以在应用程序的任何地方使用此实例
import React from 'react';
import apiClient from "./apiClient";
export default ( name ) =>
useEffect(() =>
apiClient.get(/*...*/).then(/*...*/);
);
return <h1>Hello name!</h1>;
如果您更喜欢使用 hooks,您可以轻松地将客户端封装成一个:
// apiClient.js
export function useApiClient() return apiClient;
Playgroung with full example
【讨论】:
【参考方案2】:这可能是因为useApiClient.js
中的useEffect
是异步方法。
我个人认为直接将标头分配给axios
对象并没有什么坏处。但是我看到您正在使用axios.create
,所以我假设您可能正在调用多个服务 API/端点,并且都不需要相同的标头。总之……
建议
-
使
useApiClient
成为一个简单的类,它返回带有所需标题的axios
实例。如果没有,我严重错过了在这里使用useEffect
的意义。
行为
在您归档的每个文件中调用此方法,所有文件都将具有单独的 axios
实例,而不会污染全局 axios
对象。
useApiClient.js
function useApiClient
return axios
.create(
baseURL: "http://localhost:8080/api/",
responseType: "json"
)
.interceptors.request.use(
config =>
console.log("setting up");
const token = cookies.get("token");
if (token != null)
config.headers.authorization = token;
,
error => Promise.reject(error)
);
export default useApiClient;
-
创建一个工厂类,它根据输入参数返回
axios
实例(根据传递的参数分配不同的标头并返回实例)。如果可行,您也可以将其命名为 Singleton
。
希望对你有帮助。
【讨论】:
以上是关于在自定义反应钩子中使用 axios的主要内容,如果未能解决你的问题,请参考以下文章
useEffect 在自定义钩子中使用 ref 缺少依赖项警告
如何使用 Mock Service Worker 在自定义钩子中模拟获取?