在自定义反应钩子中使用 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 在自定义钩子中模拟获取?

在自定义组件中反应原生访问 refs

如何使用自定义 React 钩子通过 Axios 发出 POST 或 DELETE 请求

反应 useState 钩子不使用 axios 调用更新

React Hooks:确保 useEffect 仅在自定义钩子的数组参数内容更改时运行的惯用方法