+5

Setup axios - chuyển trang bằng react-router-dom trong interceptor

Giới thiệu

Bài viết này giải quyết 2 vấn đề

  1. Cách cài đặt axios
  2. Các sử dụng được react-router-dom trong axios interceptor để chuyển trang mà không bị load lại cả website

Nội dung

Cài đặt axios

  • npm: npm install axios

  • yarn: yarn add axios

Chuyển trang trong interceptor bằng react-router-dom

Vấn đề

Trong reactjs hoặc trong bất kỳ single page app nào chúng ta đều không mong muốn phải re-load toàn bộ trang, việc này làm trình duyệt phải tải lại hết tài nguyên (js, css, image, font,..) làm giảm trải nghiệm người dùng mà lại không tận dụng được sức mạnh của single page app. Để di chuyển qua lại trong các page chúng ta thường sử dụng hook useHistory/useNavigate của react-router-dom, tuy nhiên interceptor của axios lại không phải là 1 react-component nên không thể sử dụng hook được. Vậy thì phải làm sao đây?

Cách giải quyết

  • Sử dụng cách khác react-router-dom mà cho phép di chuyển trang mà không cần dùng hook
  • Gọi interceptor vào trong react-component để dùng hook
Logic

Tất nhiên trong bài viết này mình sẽ giải quyết theo hướng thứ 2, mindset như sau:

  • Làm sao để set được interceptor trong component?
    • Gọi interceptor ngay App.tsx của project, khỏi tạo 1 lần duy nhất. So ease!
  • Nếu set được rồi thì làm sao những chỗ cần dùng axios sẽ phải sử dụng như thế nào
    • Để những nơi khác ( component, function) có thể dùng được thì cần dùng chung instance với axios interceptor trong App.tsx phía trên

Coding

B1: Gọi interceptor ngay trong component đầu tiên App.tsx
Ở đây mình ví dụ nếu status 401 thì sẽ redirect sang /login

import axios from 'axios';

function App() {
     const navigate = useNavigate();

  const axiosInstance = axios.create({
    baseURL: "http://localhost:3000/api",
  });

  axiosInstance.interceptors.request.use((config) => {
    // handle before request is sent
    return config;
  }, (error) => {
    // handle request error
    return Promise.reject(error);
  });

  axiosInstance.interceptors.response.use((response) => {
    // handle response data
    return response;
  }, (error) => {
    // handle response un-authen error
    if (error.response.status === 401) {
      navigate("/login");
    }
    return Promise.reject(error);
  });

  return (<RouterProvider router={routers} fallbackElement={"loading..."} />)
    }

Vậy là xong vấn đề navigate 💪

B2: Làm sao để sử dụng lại được axiosInstance vì mình chỉ custom interceptor của nó thôi, nếu dùng instance khác thì lại chẳng còn navigate nữa, chẳng nhẽ export từ App.tsx ra???

Để đưa về chung 1 instance thì có 2 cách.

  • Cách 1: Sử dụng singleton pattern
  • Cách 2: Sử dụng lại chính instance của thư viện

Ở đây mình sẽ dùng cách 2, còn cách 1 anh em tự tìm hiểu thêm nhé. Anh em chỉ cần thay axiosInstance bằng axios được import trực tiếp từ thư viện là xong

import axios from 'axios';

function App() {

  const navigate = useNavigate();

  axios.defaults.baseURL = "http://localhost:3000/api";

  axios.interceptors.request.use((config) => {
    // handle before request is sent
    return config;
  }, (error) => {
    // handle request error
    return Promise.reject(error);
  });

  axios.interceptors.response.use((response) => {
    // handle response data
    return response;
  }, (error) => {
    // handle response un-authen error
    if (error.response.status === 401) {
      navigate("/login");
    }
    return Promise.reject(error);
  });
  
    return (<RouterProvider router={routers} fallbackElement={"loading..."} />)
    }

Vậy là xong, quá đơn giản 👍

B3: Fix bug

Khi đến bước này anh em chạy chắc chắn sẽ báo lỗi vì mình sử dụng useNavigate ở ngoài RouterProvider. Vậy thì chỉ cần đưa đống code này vào trong RouterProvider là được. Ở đây mình đang dùng react-router-dom 6.8 có thể hơi khác với mọi người. Nhưng mindset là đưa phần setup interceptor vào phía trong RouterProvider để dùng được useNavigate hoặc useHistory


import axios from "axios";

const OutletContainer = () => {
    const navigate = useNavigate();

    axios.defaults.baseURL = "http://localhost:3000/api";

    axios.interceptors.request.use((config) => {
        // handle before request is sent
        return config;
    }, (error) => {
        // handle request error
        return Promise.reject(error);
    });

    axios.interceptors.response.use((response) => {
        // handle response data
        return response;
    }, (error) => {
        // handle response un-authen error
        if (error.response.status === 401) {
            navigate("/login");
        }
        return Promise.reject(error);
    });
    return <Outlet />
}


const ALL_ROUTES: RouteObject[] = [...HOME_ROUTES, ...PRODUCT_ROUTES, ...CATEGORY_ROUTES];
const CONTAINER_ROUTES: RouteObject[] = [
    {
        element: <OutletContainer />,
        children: ALL_ROUTES,
        errorElement: <ErrorPage />
    },
]

export default createBrowserRouter(CONTAINER_ROUTES);
Cách sử dụng ✅
import axios from 'axios';

  const getProducts = () => {
    return axios.get("/products");
  }

Tổng kết

Vậy là xong, chúc anh em hiểu bài. Khó chỗ nào contact với mình


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí