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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> navigate <span class="token operator">=</span> <span class="token function">useNavigate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> axiosInstance <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> baseURL<span class="token operator">:</span> <span class="token string">"http://localhost:3000/api"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axiosInstance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle before request is sent</span> <span class="token keyword">return</span> config<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle request error</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axiosInstance<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response data</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response un-authen error</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">401</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">navigate</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token operator"><</span>RouterProvider router<span class="token operator">=</span><span class="token punctuation">{</span>routers<span class="token punctuation">}</span> fallbackElement<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">"loading..."</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> navigate <span class="token operator">=</span> <span class="token function">useNavigate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>baseURL <span class="token operator">=</span> <span class="token string">"http://localhost:3000/api"</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle before request is sent</span> <span class="token keyword">return</span> config<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle request error</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response data</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response un-authen error</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">401</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">navigate</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token operator"><</span>RouterProvider router<span class="token operator">=</span><span class="token punctuation">{</span>routers<span class="token punctuation">}</span> fallbackElement<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">"loading..."</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">"axios"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">OutletContainer</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> navigate <span class="token operator">=</span> <span class="token function">useNavigate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>defaults<span class="token punctuation">.</span>baseURL <span class="token operator">=</span> <span class="token string">"http://localhost:3000/api"</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">config</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle before request is sent</span> <span class="token keyword">return</span> config<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle request error</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> axios<span class="token punctuation">.</span>interceptors<span class="token punctuation">.</span>response<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response data</span> <span class="token keyword">return</span> response<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle response un-authen error</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">.</span>response<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">401</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">navigate</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token operator"><</span>Outlet <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token constant">ALL_ROUTES</span><span class="token operator">:</span> RouteObject<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token constant">HOME_ROUTES</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token constant">PRODUCT_ROUTES</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token constant">CATEGORY_ROUTES</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">CONTAINER_ROUTES</span><span class="token operator">:</span> RouteObject<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> element<span class="token operator">:</span> <span class="token operator"><</span>OutletContainer <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">,</span> children<span class="token operator">:</span> <span class="token constant">ALL_ROUTES</span><span class="token punctuation">,</span> errorElement<span class="token operator">:</span> <span class="token operator"><</span>ErrorPage <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createBrowserRouter</span><span class="token punctuation">(</span><span class="token constant">CONTAINER_ROUTES</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Cách sử dụng
1 2 3 4 5 6 | <span class="token keyword">import</span> axios <span class="token keyword">from</span> <span class="token string">'axios'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">getProducts</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"/products"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Tổng kết
Vậy là xong, chúc anh em hiểu bài.