我有一个 axios 实例,需要配置它才能开始进行正常的 API 调用。我需要设置授权标头并放置 1 个拦截器,该拦截器将在每个 401 请求上刷新 JWT 令牌。因为我使用的是钩子,所以我将其制作成一个单独的组件,然后将其嵌套在我的 App.tsx 中。
我正在使用react-router-dom 中支持“加载器”功能的数据路由器之一。问题是,当我刷新路由页面(假设“/profile”)时,它首先调用加载器函数,然后才调用所有 useEffect 实例。它显然会导致 401 错误,即使在 Axios 组件渲染时用户已在 useAuth() 挂钩中设置。
那家伙也有和我类似的问题,但我在评论中没有找到合适的答案
创建路由器时,React Router Dom V6 加载程序会触发
正如他所说,我什至可以从我的 App 组件返回语句中删除
这是视频,如果它能让您更好地理解这个问题:
https://youtu.be/MoV_924owTU
任何帮助都可以!
AxiosSettings.tsx:
import axios from 'axios';
import useAuth from '../hooks/useAuth';
import {useEffect} from 'react';
const api = axios.create({
baseURL: 'http://127.0.0.1:8000/api/',
headers: {
"Content-Type": "application/json",
},
})
export function AxiosSettings({children}: { children: ReactNode }) {
// Using this only because I need to call useAuth() hook in order to get user
const {user, login} = useAuth()
useEffect(() => {
console.log('effect 1')
if (user) {
api.defaults.headers.common["Authorization"] = `Bearer ${user.accessToken}`
} else {
delete api.defaults.headers.common["Authorization"]
}
}, [user])
useEffect(() => {
console.log('effect 2')
const interceptor = api.interceptors.response.use(...)
return api.interceptors.response.eject(interceptor)
}, []);
return children;
}
export default api
我的应用程序组件的结构如下:
const router = createBrowserRouter(createRoutesFromElements(
<Route element={<RootLayout/>}>
<Route index element={<Home/>}/>
<Route path="register" element={<Signup/>}/>
<Route path="login" element={<Login/>}/>
<Route path="about"/>
<Route element={<RequireAuth/>}>
<Route path="profile" element={<ProfileLayout/>} loader={ProfileLoader}>
<Route index element={<ProfileIndex/>} loader={PrizesLoader}/>
<Route path="history"/>
<Route path="invitations"/>
</Route>
</Route>
<Route path="admin" element={<RequireAdmin/>}>
<Route index element={<AdminPage/>}/>
<Route path="game/:hash" element={<Game/>}/>
</Route>
</Route>
))
function App() {
const [user, setUser] = useState<User | null>(() => {
return SessionStorageUserService.get();
})
return (
<AuthContext.Provider value={{user, setUser}}>
<AxiosSettings>
<RouterProvider router={router} />
</AxiosSettings>
</AuthContext.Provider>
)
}
仅当 axios 效果已运行并设置标头并附加响应拦截器时,
AxiosSettings
才能有条件地渲染其 children
。
export function AxiosSettings({ children }: { children: ReactNode }) {
const [isLoaded, setIsLoaded] = useState(false);
const { user, login } = useAuth();
useEffect(() => {
console.log('effect 1');
if (user) {
api.defaults.headers.common["Authorization"] = `Bearer ${user.accessToken}`;
} else {
delete api.defaults.headers.common["Authorization"];
}
}, [user]);
useEffect(() => {
console.log('effect 2');
const interceptor = api.interceptors.response.use(...);
setIsLoaded(true);
return api.interceptors.response.eject(interceptor);
}, []);
return isLoaded
? children
: null; // <-- or loading indicator/spinner/etc
}
RouterProvider
将有条件地渲染,并且它和用户所在的路线将不会被渲染,直到至少配置了 axios api
实例后的第二个渲染周期。