Giới thiệu
Bài viết này giải quyết 2 vấn đề
- Cách cài đặt axios
- 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