在自定义React挂钩中使用axios

时间:2020-04-06 11:38:29

标签: reactjs cookies axios

我想用拦截器创建一个全局axios实例,该实例添加带有访问令牌的标头。 访问令牌存储在cookie中,但是我无法弄清楚如何在拦截器内部访问cookie,因为它不是React组件。

我试图在这样的自定义钩子中创建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不是函数

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:4)

问题是您试图使用react-cookie来提供用于处理组件中cookie的api。在某些情况下可能会很好,但是对于当您需要在每个请求上检查cookie的情况下,它会强制将axios实例放置在组件层次结构的顶部,并且使在组件之外使用该实例更加困难(最有可能将实例传递给外部函数)。

我建议在一个单独的模块中创建一个实例,并使用其他工具来获取universal-cookie之类的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>;
}

如果您希望坚持使用钩子,则可以轻松地将客户包装成一个:

// apiClient.js
export function useApiClient() { return apiClient; } 

Playgroung with full example

答案 1 :(得分:0)

这也许是因为useEffect中的useApiClient.js是异步方法。

我个人认为直接将标题分配给axios对象没有什么害处。但是,我看到您正在使用axios.create,因此我假设您可能正在调用多个Service API /端点,并且所有这些都不需要相同的标头。反正...

建议

  1. 使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;
  1. 创建一个工厂类,该工厂类根据输入参数返回axios实例(以根据传递的参数分配不同的标头并返回该实例)。如果可行,您也可以将其设为Singleton

希望有帮助。

相关问题