Получение React Hook «useSetupInterceptors» не может быть вызвано на верхнем уровне при попытке использовать перехват useSignOut

Я пытаюсь изучить приложение React JS и Springboot, и при перезапуске Java я аннулирую tokenVersion (метку времени в полезной нагрузке jwt, но не фактический токен) в токене jwt.

при перезапуске Java и если я пытаюсь перезагрузить страницу реагирования или навигацию по Spring Security, выдается ошибка 403.

используя ответ, который я проверяю, и пытаюсь заставить пользователя выйти из системы и попытаться использовать перехватчик SignOut для очистки состояния или сеанса, но я получаю ошибку перехватчика реакции.

Я пробовал много способов из чата gpt, но ни один из них не дал решения.

это мои следующие коды реакции:

1. Конечная точкаConfig.js

import axios from 'axios';
import useSetupInterceptors from '../util/useSetupInterceptors'
const config = window.APP_CONFIG;
const endPointURLIP = config.endPointIP;
const defaultUrl = "localhost";
if (!endPointURLIP)
  endPointURLIP = defaultUrl;

  //Retrieving Token from the cookie which saved while login.
  const getTokenFromCookie = () => {
      const cookies = document.cookie.split(';').map(cookie => cookie.trim());
      for (const cookie of cookies) {
          if (cookie.startsWith('_auth=')) {
              const tokenCookie = cookie.split('=');
              if (tokenCookie.length === 2) {
                  return tokenCookie[1];
              }
          }
      }

      return null; // Token not found in cookies
  };
  const authToken = getTokenFromCookie();

  const getAxiosHeader = () => {
    const API_BASE_URL = 'http://'+endPointURLIP+':9081';
    const AUTHENTICATION_HEADER_TOKEN = authToken ? `Bearer ${authToken}` : null;

    return axios.create({
      baseURL: API_BASE_URL,
      headers: {
        Authorization: AUTHENTICATION_HEADER_TOKEN,
      },
    });
  };
  const authAxiosHeader = getAxiosHeader();
  useSetupInterceptors(authAxiosHeader);


  const endpointConfig = {
    a: 'http://'+endPointURLIP+':9081',
     HISTORY_ENDPOINT: '/demo/history',

    AUTHENTICATION_HEADER_TOKEN: authToken ? `Bearer ${authToken}` : null,
    AUTHENTICATION_HEADER:authAxiosHeader,
  };

export default endpointConfig;

2. используйтеSetupInterceptors.js

import { useEffect } from 'react';
import { useSignOut } from 'react-auth-kit';

const useSetupInterceptors = ({ axiosInstance }) => {
  const signOut = useSignOut();
  useEffect(() => {
    const interceptor = axiosInstance.interceptors.response.use(
      (response) => response,
      (error) => {
        if (error.response && error.response.status === 403) {
          alert('Request failed with status code 403:', error.message);
          handleLogout();
        }
        return Promise.reject(error);
      }
    );


  const handleLogout = () => {
      window.location.href = '/';
      signOut();
  };
 return () => {
 axiosInstance.interceptors.response.eject(interceptor);
 };
 }, [axiosInstance, signOut]);

};

export default useSetupInterceptors;

3. Демосервис.js

import axios from 'axios';
import endpointConfig from './EndpointConfig';

class DemoService {

  constructor() {
    this.baseUrl = endpointConfig.API_BASE_URL;
    this.getHistoryEndpoint = endpointConfig.HISTORY_ENDPOINT;
    this.axiosHeader = endpointConfig.AUTHENTICATION_HEADER;
  }
  async getDemoHistory() {
    try {
      const response = await this.axiosHeader.get(`${this.getHistoryEndpoint}`);
      const configData = response.data;
      return configData;
    } catch (error) {
      console.info('Error retrieving data:'+error);
    }
  }
}
export default new DemoService();

Демоистория.js

import React, { Component } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import DemoService from '../services/DemoService';

class DemoHistory extends Component {
  constructor(props) {
    super(props);
    this.state = {
      resultFromController: [],
      isLoading: true,
    };
  }

  componentDidMount() {
    this.getDemoHistory();
  }

  getDemoHistory = () => {
    DemoService.getDemoHistory()
      .then((res) => {
        this.setState({ resultFromController: res, isLoading: false });
      })
      .catch((error) => {
        console.error('Error fetching Demo history:', error);
        this.setState({ isLoading: false });
      });
  };

  render() {
    const { resultFromController, isLoading } = this.state;

    if (isLoading) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <h1>Demo History</h1>
        <DataTable value = {resultFromController}>
          <Column field = "fromUser" header = "From User" />
          <Column field = "toUser" header = "To User" />
        </DataTable>
      </div>
    );
  }
}

export default DemoHistory;

Приложение.js

import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { AuthProvider, RequireAuth } from 'react-auth-kit';
import DemoHistory from './components/DemoHistory';

function App() {
  return (
    <AuthProvider
      authType = {"cookie"}
      authName = {"_auth"}
      cookieDomain = {window.location.hostname}
      cookieSecure = {false}
    >
      <div>
        <h2 className = "header_name">Demo</h2>
        <BrowserRouter>
          <Routes>
            <Route path = "DemoHistory" element = {<RequireAuth loginPath = "/"><DemoHistory /></RequireAuth>} />
          </Routes>
        </BrowserRouter>
      </div>
    </AuthProvider>
  );
}

export default App;

Логин.js

import React, { useState } from "react";
import { Button, Form } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import { useNavigate } from "react-router-dom";
import { useAuthHeader, useSignIn ,useSignOut } from "react-auth-kit";
import LoginService from "../services/LoginService";

export default function Login() {
  const [loginId, setLoginId] = useState("");
  const [password, setPassword] = useState("");
  const loginService = new LoginService();
  const navigate = useNavigate();
  const { setAuth, setHeader } = useAuthHeader();
  const signIn = useSignIn();
  const [loginStatus, setLoginStatus] = useState(null);

  function validate() {
    return loginId.length > 0 && password.length > 0;
  }

  async function submit(event) {
  try{
    event.preventDefault();
    loginService.setLoginId(loginId);
    loginService.setPassword(password);
    const result = await loginService.validateLogin(loginId,password);
    if (result && result.response === "success") {
          signIn({
            token: result.token,
            expiresIn: 24*60*60,//24 hours
            tokenType: "Bearer",
            authState: { username: "admin" },
          });
          window.location.href = "/history";
    } else {
      setLoginStatus("invalid");
      console.info("Login failed...");
    }
    }catch(error){
        setLoginStatus("invalid");
        console.info("Error While login."+error);
    }
  }

  return (
    <form
      name = "vishing_form"
      method = "post"
      className = "login_tab_form"
      onSubmit = {submit}
    >
      <p align = "center" style = {{ fontWeight: 'bolder', fontSize: '18px', fontFamily: 'helvetica' }}>
        RedShift Login
      </p>
      <div>
        <label>Login ID</label>
        <input className = "loginTextField"
          autoFocus
          type = "text"
          value = {loginId}
          onChange = {(e) => setLoginId(e.target.value)}
        />
      </div>
      <div>
        <label>Password</label>
        <input className = "loginTextField"
          type = "password"
          value = {password}
          onChange = {(e) => setPassword(e.target.value)}
        />
      </div>
          {loginStatus === "invalid" && (
            <p>
              Invalid credentials. Please try again.
            </p>
          )}
      <div>
        <button
          id = "submit"
          type = "submit"
          name = "login"
          className = "submitBtn"
          disabled = {!validate()}
        >
          Login
        </button>
      </div>
      </div>
    </form>
  );
}

ошибка:

Compiled with problems:
ERROR
[eslint] 
src\services\EndpointConfig.js
  Line 38:3:  React Hook "useSetupInterceptors" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function  react-hooks/rules-of-hooks

я пытаюсь изучить реакцию js, пожалуйста, помогите, как решить эту проблему или какой-либо лучший способ справиться с управлением сеансом и обработкой закрытия браузера

Спасибо

Вы не можете вызывать перехватчики React вне функциональных компонентов React или пользовательских перехватчиков. Однако я не вижу в вашем посте никакого кода React, кроме хука и файла useSetupInterceptors. Можете ли вы отредактировать , чтобы включить более полный минимально воспроизводимый пример?

Drew Reese 29.03.2024 19:32

@DrewReese на самом деле файл EndpointConfig.js содержит объявление URL-адреса конечной точки, а также кадрирование заголовка axios с токеном jwt

Harish Mahi 02.04.2024 17:47

Хорошо, это нормально… но ничего из этого не является кодом React, поэтому он не может использовать перехватчик React. Если useSetupInterceptors — это то, что вы хотите использовать для установки/настройки перехватчиков Axios, вам нужно будет сделать это в React-land, и если вам нужна дополнительная помощь с этим отсюда, нам нужно будет увидеть ваш код React, следовательно, полный минимальный воспроизводимый пример соответствующего кода, с которым вы работаете.

Drew Reese 02.04.2024 19:35

теперь добавлено больше кода, моя цель состоит в том, чтобы при перезапуске приложения (Java) необходимо было заставить пользователя выйти из системы и очистить сеансы, по этой причине я пытаюсь использовать перехватчик реакции SignOut на основе ответа Axios

Harish Mahi 03.04.2024 06:18

@DrewReese, я не могу открыть диалоговое окно, братан, из useSetupInterceptors

Harish Mahi 03.04.2024 09:09

Похоже, это новая/отличающаяся проблема/проблема, о которой идет речь в этом посте. Предпочтительно создать новую публикацию SO, посвященную новой проблеме, по которой вы просите помощи. Если вы создадите новый пост, не стесняйтесь написать мне здесь в комментарии со ссылкой на пост, и я смогу посмотреть, когда он будет доступен, в противном случае вам повезет, если вы привлечете больше внимания к вашей проблеме и получите помощь быстрее, чем спрашиваю об этом здесь, в комментариях.

Drew Reese 03.04.2024 09:12

окей, братан, я создам новый

Harish Mahi 03.04.2024 09:16
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
7
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете вызывать перехватчики React вне компонентов React и пользовательских перехватчиков React. Вы можете переместить вызов useSetupInterceptors в свой код React.

Вот пример использования локального компонента, который отображается внутри AuthProvider, чтобы он мог вызывать крючок useSetupInterceptors, а крючок useSetupInterceptors мог ссылаться на крючок useSignOut из `react-auth-kit';

Конечная точкаConfig.js

import axios from 'axios';
import useSetupInterceptors from '../util/useSetupInterceptors';

const config = window.APP_CONFIG;
const endPointURLIP = config.endPointIP;
const defaultUrl = "localhost";

if (!endPointURLIP) {
  endPointURLIP = defaultUrl;
}

const getTokenFromCookie = () => {
  const [, token = null] = document.cookie
    .split(";")
    .map(cookie => cookie.trim().split(" = "))
    .find(([key, value]) => key === "_auth") ?? [];
  return token;
};

const authToken = getTokenFromCookie();

const getAxiosInstance = () => {
  const API_BASE_URL = 'http://' + endPointURLIP + ':9081';
  const AUTHENTICATION_HEADER_TOKEN = authToken ? `Bearer ${authToken}` : null;

  return axios.create({
    baseURL: API_BASE_URL,
    headers: {
      Authorization: AUTHENTICATION_HEADER_TOKEN,
    },
  });
};

export const authAxiosInstance = getAxiosInstance(); // <-- export for app usage

const endpointConfig = {
  a: 'http://'+endPointURLIP+':9081',
  HISTORY_ENDPOINT: '/demo/history',
  AUTHENTICATION_HEADER_TOKEN: authToken ? `Bearer ${authToken}` : null,
  AUTHENTICATION_HEADER: authAxiosInstance,
};

export default endpointConfig;
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { AuthProvider, AuthOutlet } from 'react-auth-kit';
import DemoHistory from './components/DemoHistory';
import useSetupInterceptors from '../util/useSetupInterceptors';
import { authAxiosInstance } from './EndpointConfig';

const SetupAxios = () => {
  useSetupInterceptors(authAxiosInstance);

  return null;
};

function App() {
  return (
    <AuthProvider
      authType = {"cookie"}
      authName = {"_auth"}
      cookieDomain = {window.location.hostname}
      cookieSecure = {false}
    >
      <BrowserRouter>
        <SetupAxios />
        <div>
          <h2 className = "header_name">Demo</h2>
          <Routes>
            <Route element = {<AuthOutlet fallbackPath='/' />}>
              <Route path = "DemoHistory" element = {<DemoHistory />} />
            </Route>
            ...
          </Routes>
        </div>
      </BrowserRouter>
    </AuthProvider>
  );
}

export default App;

это работает, и я пытаюсь открыть диалоговое окно из useSetupInterceptors, но оно не работает. const useSetupInterceptors = (authAxiosHeader) => {const [showDialog, setShowDialog] = useState(false); if (error.response && error.response.status === 403) { alert("вызов") setShowDialog(true); } return ( <> <Dialogvisible = {showDialog} header = "Expired" onHide = {handleLogoutButtonClick} draggable = {false} > <h5 style = {{marginTop: '3rem' }}>войдите снова.</h5> </Диалог> </> );

Harish Mahi 03.04.2024 09:05

@HarishMahi Что такое error в useSetupInterceptors? Похоже, у вас возникли непреднамеренные побочные эффекты при попытке открыть предупреждение браузера, поставить в очередь обновление состояния и вернуть немного JSX? Немного непонятно, что вы делаете в этом коде в комментарии. Имеет ли это отношение к вашему сообщению, в котором вы пытаетесь создать экземпляры перехватчиков Axios? Похоже, это дополнительный вопрос/проблема для другого поста.

Drew Reese 03.04.2024 09:09

это следующий вопрос, брат, можно ли вернуть jsx из useSetupInterceptors вместо прямого перенаправления пользователя, я хочу показать всплывающее окно или диалоговое окно, и я попробовал, но он не открывается

Harish Mahi 03.04.2024 09:15

Другие вопросы по теме