React页面刷新后Pet-finder API请求无法获取Access Token问题
问题
使用Pet-finder API开发React应用,将Access Token存储在Context中。特定动物页面首次渲染大多正常,但刷新页面时会抛出Axios 401错误(API文档标注为“Access was denied due to invalid credentials”),请求头中Authorization字段显示为“undefined undefined”。尝试将token添加到请求所用useEffect的依赖数组中,虽能成功加载数据,但刷新时控制台仍会报错。当前仅该页面会在渲染后立即发起请求,怀疑此场景与问题相关。
相关代码:
TokenContext.jsx
const TokenContext = createContext(); export const TokenProvider = ({ children }) => { const [token, setToken] = useState({}); let location = useLocation(); const fetchToken = async () => { await axios .get("http://localhost:3001/fetchToken") .then((res) => { const newToken = { token: res.data.access_token, tokenType: res.data.token_type, expires: new Date().getTime() + res.data.expires_in * 1000, }; localStorage.setItem("token", JSON.stringify(newToken)); }) .catch((error) => { console.log(error); }); }; useEffect(() => { const foundToken = JSON.parse(localStorage.getItem("token")); if (!foundToken || token.expires - new Date().getTime() < 10) { fetchToken(); } else { setToken(foundToken); } }, [location.pathname]); return ( <TokenContext.Provider value={{ token }}>{children}</TokenContext.Provider> ); }; export default TokenContext;
Pet.jsx
const Pet = () => { const { id } = useParams(); const { token } = useContext(TokenContext); const [isLoading, setIsLoading] = useState(false); const [pet, setPet] = useState({}); useEffect(() => { setIsLoading(true); petFinderApi .get(`/animals/${id}`, { headers: { Authorization: `${token.tokenType} ${token.token}`, }, }) .then((res) => { setPet(res.data.animal); setIsLoading(false); }) .catch((error) => { console.log(error); }); }, [token]); if (!pet) { return ( isLoading ?? ( //Spinner renders here ) ); } else { return ( //Stuff will render here ) } }; export default Pet;
问题分析
- Token初始化时机问题:刷新页面时,Context中的
token初始值为空对象{},Pet组件会立即发起请求,导致token.tokenType和token.token均为undefined,Authorization头无效,触发401错误。 - fetchToken未同步Context状态:
fetchToken仅将token存入localStorage,未调用setToken更新Context状态,导致新token获取后,Context无法同步,后续请求仍可能使用空值。 - 有效期判断逻辑异常:初始
token为空对象,token.expires为undefined,判断token.expires - new Date().getTime() <10时会产生NaN,导致逻辑判断出错。 - 请求未做token有效性校验:Pet组件未判断token是否有效就发起请求,直接使用空token触发错误。
解决方案
1. 修复fetchToken函数,同步Context状态
在获取新token后,不仅存入localStorage,还要更新Context的token状态:
const fetchToken = async () => { try { const res = await axios.get("http://localhost:3001/fetchToken"); const newToken = { token: res.data.access_token, tokenType: res.data.token_type, expires: new Date().getTime() + res.data.expires_in * 1000, }; localStorage.setItem("token", JSON.stringify(newToken)); setToken(newToken); // 同步到Context } catch (error) { console.log(error); } };
2. 修正Token有效期判断逻辑
改用foundToken的有效期进行判断,避免使用初始空token的属性:
useEffect(() => { const foundToken = JSON.parse(localStorage.getItem("token")); const currentTime = new Date().getTime(); if (!foundToken || (foundToken.expires - currentTime) < 10) { fetchToken(); } else { setToken(foundToken); } }, [location.pathname]);
3. Pet组件添加Token有效性校验
发起请求前先确认token是否有效,避免空token请求:
useEffect(() => { if (!token.tokenType || !token.token) return; setIsLoading(true); petFinderApi .get(`/animals/${id}`, { headers: { Authorization: `${token.tokenType} ${token.token}`, }, }) .then((res) => { setPet(res.data.animal); setIsLoading(false); }) .catch((error) => { console.log(error); setIsLoading(false); // 出错后终止加载状态 }); }, [token, id]);
4. 优化加载状态逻辑
修正加载状态判断,避免空值渲染问题:
if (isLoading) { return ( // 渲染加载Spinner ); } if (!Object.keys(pet).length) { return <div>暂无宠物信息</div>; } return ( // 渲染宠物详情内容 );
内容的提问来源于stack exchange,提问作者CoffeeSlurper
相关产品推荐
相关产品推荐

