Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

1.7K
Views
¿Cómo configurar correctamente los interceptores Axios con React Context?

Dado que quiero configurar los interceptores Axios con React Context, la única solución que parece viable es crear un componente Interceptor para usar el gancho useContext para acceder al estado y envío de Contexto.

El problema es que esto crea un cierre y devuelve datos antiguos al interceptor cuando se lo llama.

Estoy usando la autenticación JWT usando React/Node y estoy almacenando tokens de acceso usando la API de contexto.

Así es como se ve mi componente Interceptor en este momento:

 import React, { useEffect, useContext } from 'react'; import { Context } from '../../components/Store/Store'; import { useHistory } from 'react-router-dom'; import axios from 'axios'; const ax = axios.create(); const Interceptor = ({ children }) => { const [store, dispatch] = useContext(Context); const history = useHistory(); const getRefreshToken = async () => { try { if (!store.user.token) { dispatch({ type: 'setMain', loading: false, error: false, auth: store.main.auth, brand: store.main.brand, theme: store.main.theme, }); const { data } = await axios.post('/api/auth/refresh_token', { headers: { credentials: 'include', }, }); if (data.user) { dispatch({ type: 'setStore', loading: false, error: false, auth: store.main.auth, brand: store.main.brand, theme: store.main.theme, authenticated: true, token: data.accessToken, id: data.user.id, name: data.user.name, email: data.user.email, photo: data.user.photo, stripeId: data.user.stripeId, country: data.user.country, messages: { items: [], count: data.user.messages, }, notifications: store.user.notifications.items.length !== data.user.notifications ? { ...store.user.notifications, items: [], count: data.user.notifications, hasMore: true, cursor: 0, ceiling: 10, } : { ...store.user.notifications, count: data.user.notifications, }, saved: data.user.saved.reduce(function (object, item) { object[item] = true; return object; }, {}), cart: { items: data.user.cart.reduce(function (object, item) { object[item.artwork] = true; return object; }, {}), count: Object.keys(data.user.cart).length, }, }); } else { dispatch({ type: 'setMain', loading: false, error: false, auth: store.main.auth, brand: store.main.brand, theme: store.main.theme, }); } } } catch (err) { dispatch({ type: 'setMain', loading: false, error: true, auth: store.main.auth, brand: store.main.brand, theme: store.main.theme, }); } }; const interceptTraffic = () => { ax.interceptors.request.use( (request) => { request.headers.Authorization = store.user.token ? `Bearer ${store.user.token}` : ''; return request; }, (error) => { return Promise.reject(error); } ); ax.interceptors.response.use( (response) => { return response; }, async (error) => { console.log(error); if (error.response.status !== 401) { return new Promise((resolve, reject) => { reject(error); }); } if ( error.config.url === '/api/auth/refresh_token' || error.response.message === 'Forbidden' ) { const { data } = await ax.post('/api/auth/logout', { headers: { credentials: 'include', }, }); dispatch({ type: 'resetUser', }); history.push('/login'); return new Promise((resolve, reject) => { reject(error); }); } const { data } = await axios.post(`/api/auth/refresh_token`, { headers: { credentials: 'include', }, }); dispatch({ type: 'updateUser', token: data.accessToken, email: data.user.email, photo: data.user.photo, stripeId: data.user.stripeId, country: data.user.country, messages: { items: [], count: data.user.messages }, notifications: store.user.notifications.items.length !== data.user.notifications ? { ...store.user.notifications, items: [], count: data.user.notifications, hasMore: true, cursor: 0, ceiling: 10, } : { ...store.user.notifications, count: data.user.notifications, }, saved: data.user.saved, cart: { items: {}, count: data.user.cart }, }); const config = error.config; config.headers['Authorization'] = `Bearer ${data.accessToken}`; return new Promise((resolve, reject) => { axios .request(config) .then((response) => { resolve(response); }) .catch((error) => { reject(error); }); }); } ); }; useEffect(() => { getRefreshToken(); if (!store.main.loading) interceptTraffic(); }, []); return store.main.loading ? 'Loading...' : children; } export { ax }; export default Interceptor;

Se llama a la función getRefreshToken cada vez que un usuario actualiza el sitio web para recuperar un token de acceso si hay un token de actualización en la cookie.

La función interceptTraffic es donde persiste el problema. Consiste en un interceptor de solicitudes que agrega un encabezado con el token de acceso a cada solicitud y un interceptor de respuesta que se usa para manejar el vencimiento del token de acceso para obtener uno nuevo usando un token de actualización.

Notará que estoy exportando ax (una instancia de Axios donde agregué interceptores) pero cuando se llama fuera de este componente, hace referencia a datos antiguos de la tienda debido al cierre.

Obviamente, esta no es una buena solución, pero es por eso que necesito ayuda para organizar los interceptores sin dejar de poder acceder a los datos de contexto.

Tenga en cuenta que creé este componente como un envoltorio, ya que representa los elementos secundarios que se le proporcionan, que es el componente principal de la aplicación.

Cualquier ayuda es apreciada, gracias.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Enfoque común (almacenamiento local)

Es una práctica común almacenar el JWT en localStorage con

 localStorage.setItem('token', 'your_jwt_eykdfjkdf...');

al iniciar sesión o actualizar la página, y cree un módulo que exporte una instancia de Axios con el token adjunto. Obtendremos el token de localStorage

custom-axios.js

 import axios from 'axios'; // axios instance for making requests const axiosInstance = axios.create(); // request interceptor for adding token axiosInstance.interceptors.request.use((config) => { // add token to request headers config.headers['Authorization'] = localStorage.getItem('token'); return config; }); export default axiosInstance;

Y luego, simplemente importe la instancia de Axios que acabamos de crear y realice solicitudes.

 import axios from './custom-axios'; axios.get('/url'); axios.post('/url', { message: 'hello' });

Otro enfoque (cuando tienes un token almacenado en el estado)

Si tiene su JWT almacenado en el estado o puede obtener un token nuevo del estado, cree un módulo que exporte una función que tome el token como argumento y devuelva una instancia de axios con el token adjunto de esta manera:

custom-axios.js

 import axios from 'axios'; const customAxios = (token) => { // axios instance for making requests const axiosInstance = axios.create(); // request interceptor for adding token axiosInstance.interceptors.request.use((config) => { // add token to request headers config.headers['Authorization'] = token; return config; }); return axiosInstance; }; export default customAxios;

Y luego importe la función que acabamos de crear, tome el token del estado y haga solicitudes:

 import axios from './custom-axios'; // logic to get token from state (it may vary from your approach but the idea is same) const token = useSelector(token => token); axios(token).get('/url'); axios(token).post('/url', { message: 'hello' });
over 4 years ago · Santiago Trujillo Report

0

Tengo una plantilla que funciona en un sistema con millones de accesos todos los días.

Esto resolvió mis problemas con el token de actualización y volvió a intentar la solicitud sin fallar

Primero tengo un "api.js" con axios, configuraciones, direcciones, encabezados. En este archivo hay dos métodos, uno con autenticación y otro sin ella. En este mismo archivo configuré mi interceptor:

 import axios from "axios"; import { ResetTokenAndReattemptRequest } from "domain/auth/AuthService"; export const api = axios.create({ baseURL: process.env.REACT_APP_API_URL, headers: { "Content-Type": "application/json", }, }); export const apiSecure = axios.create({ baseURL: process.env.REACT_APP_API_URL, headers: { Authorization: "Bearer " + localStorage.getItem("Token"), "Content-Type": "application/json", }, export default api; apiSecure.interceptors.response.use( function (response) { return response; }, function (error) { const access_token = localStorage.getItem("Token"); if (error.response.status === 401 && access_token) { return ResetTokenAndReattemptRequest(error); } else { console.error(error); } return Promise.reject(error); } );

Luego, el método ResetTokenAndReattemptRequest. Lo coloqué en otro archivo, pero puedes colocarlo donde quieras:

 import api from "../api"; import axios from "axios"; let isAlreadyFetchingAccessToken = false; let subscribers = []; export async function ResetTokenAndReattemptRequest(error) { try { const { response: errorResponse } = error; const retryOriginalRequest = new Promise((resolve) => { addSubscriber((access_token) => { errorResponse.config.headers.Authorization = "Bearer " + access_token; resolve(axios(errorResponse.config)); }); }); if (!isAlreadyFetchingAccessToken) { isAlreadyFetchingAccessToken = true; await api .post("/Auth/refresh", { Token: localStorage.getItem("RefreshToken"), LoginProvider: "Web", }) .then(function (response) { localStorage.setItem("Token", response.data.accessToken); localStorage.setItem("RefreshToken", response.data.refreshToken); localStorage.setItem("ExpiresAt", response.data.expiresAt); }) .catch(function (error) { return Promise.reject(error); }); isAlreadyFetchingAccessToken = false; onAccessTokenFetched(localStorage.getItem("Token")); } return retryOriginalRequest; } catch (err) { return Promise.reject(err); } } function onAccessTokenFetched(access_token) { subscribers.forEach((callback) => callback(access_token)); subscribers = []; } function addSubscriber(callback) { subscribers.push(callback); }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!