axios 拦截器在React Native Android上不会立即生效,

uujelgoq  于 5个月前  发布在  iOS
关注(0)|答案(5)|浏览(105)

描述的问题
我们正在使用auth0 API和React Native应用程序,并使用拦截器将auth令牌注入每个请求的头部。我们遇到的问题是,在Android(模拟器和设备上)上,拦截器似乎不会立即生效,因此我们发出的第一个请求没有被拦截。
我们可以使用一个肮脏的setTimeout技巧来延迟渲染使API调用的组件几百毫秒,但这感觉像是一个脆弱的解决方法。

重现问题

重现这个问题确实需要一个异步API,如react-native-auth0。因为这个API是基于钩子的,所以我们使用useEffectchildren一起 Package 组件以安装和删除拦截器。在初始化过程中,我们设置另一个状态标志,表示不要在添加拦截器后立即渲染children。然而,在安装拦截器后立即渲染才是痛苦的地方。
我们使用@tanstack/react-query进行API调用,当我们的主页面渲染时,我们会发起一个获取数据的调用。这是第一个未被拦截的调用。在应用程序流程中的后续调用工作正常。如果API不可用并且我们让查询重试,那么随后的重试会被拦截。
所以我们的特殊组件用于安装拦截器:

function AuthGuard(props) {
  const [init, setInit] = React.useState(false);
  const auth0 = useAuth0();

  React.useEffect(() => {
    const requestInterceptor = axios.interceptors.request.use(
      async (config) => {
        // Do we have a token?
        const hasCredentials = await auth0.hasValidCredentials();
        if (hasCredentials) {
          try {
            // Get the access token from auth0
            const credentials = await auth0.getCredentials();
            // Add the access token to the request
            if (credentials) {
              config.headers.Authorization = `Bearer ${credentials.accessToken}`;
            }
          } catch (err) {
            console.log(err);
          }
        }
        return config;
      },
      (error) => Promise.reject(error)
    );    

    // Without this timeout the first request made when rendering the children on Android will not be intercepted
    setTimeout(() => {
      setInit(true);
    }, 100);

    return () => {
      setInit(false);
      axios.interceptors.request.eject(requestInterceptor);
    };
  }, [auth0]);

  return init ? props.children : null;
}

然后我们用这个组件包裹我们的应用程序,如下所示:

export default function App() {
  return (
    <Auth0Provider
      domain="OUR_DOMAIN"
      clientId="OUR_CLIENT_ID"
    >
      <QueryClientProvider client={queryClient}>
        <AuthGuard>
          <HomeScreen />
        </AuthGuard>
      </QueryClientProvider>
    </Auth0Provider>
  );
}

我们的HomeScreen如下所示:

export function Home() {
  const query = useQuery({
    queryKey: ["our-query"],
    queryFn: async () => {
      const res = await axios.get("https://api.backend.dev/our-query");
      return res.data;
    },
  });

  return <Text>Hello, world!</Text>
}

代码片段

  • 无响应*

预期行为

我们希望在用axios.interceptors.request.use安装拦截器后,拦截器能够立即开始工作。理想情况下,我们可以等待拦截器安装完成,以避免使用一个讨厌的setTimeout来延迟渲染子组件。

Axios版本

1.5.0

适配器版本

  • 无响应*

浏览器

  • 无响应*

浏览器版本

  • 无响应*

Node.js版本

18.17.1

OS

Android 13

其他库版本

react-native@0.72.4
expo@49.0.10

其他上下文/截图

  • 无响应*
ecr0jaav

ecr0jaav1#

你好,@robcaldecott
你确定在第一次axios调用之前调用了'useEffect'吗?
似乎与#5843(评论)有关。

dzjeubhm

dzjeubhm2#

我认为你应该在auth0未准备好时跳过设置拦截器,通过检查auth0.isLoading

7uhlpewt

7uhlpewt3#

尝试这个

React.useEffect(() => {
    if(auth0.isLoading) return; // add this line
    const requestInterceptor = axios.interceptors.request.use(
      async (config) => {
6xfqseft

6xfqseft4#

你好,@robcaldecott 。你确定在第一次axios调用之前调用了'useEffect'吗?这似乎与#5843有关(评论)
我绝对肯定。这就是为什么我使用了那个init状态,它最初是false

4zcjmb1e

4zcjmb1e5#

我通过在模块级别安装拦截器来解决这个问题,基于对这个问题的回应:
https://community.auth0.com/t/how-to-get-token-from-outside-react-components/68756/3

class Interceptor {
  auth0 = undefined;

  setAuth0(auth0) {
    this.auth0 = auth0;
  }

  requestInterceptor = async (config) => {
    if (this.auth0) {
      const hasCredentials = await this.auth0.hasValidCredentials();
      if (hasCredentials) {
        try {
          const credentials = await this.auth0.getCredentials();
          if (credentials) {
            config.headers.Authorization = `Bearer ${credentials.accessToken}`;
          }
        } catch (err) {
          console.log(err);
        }
      }
    }
    return config;
  };
}

const interceptor = new Interceptor();
axios.interceptors.request.use(interceptor.requestInterceptor);

export function AuthGuard(props) {
  const [init, setInit] = React.useState(false);
  const auth0 = useAuth0();

  React.useEffect(() => {
    if (auth0.isLoading) {
      return;
    }
    interceptor.setAuth0(auth0);
    setInit(true);

    return () => {
      setInit(false);
      interceptor.setAuth0(undefined);
    };
  }, [auth0]);

  return init ? props.children : null;
}

这意味着 axios.interceptors.request.use 会在应用加载时立即被调用,这似乎足以解决Android问题。

相关问题