Я делаю приложение React, используя API Pet-finder. Я храню свой токен доступа внутри контекста. Когда я отображаю конкретную страницу с животными, она отображается нормально (в большинстве случаев, если предположить, что из-за той же проблемы), но когда я обновляю страницу, она выдает ошибку axios с status: 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.info(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.info(error);
});
}, [token]);
if (!pet) {
return (
isLoading ?? (
//Spinner renders here
)
);
} else {
return (
//Stuff will render here
)
}
};
export default Pet;



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Токен изначально является пустым объектом:
const [token, setToken] = useState({});
Пока вы извлекаете токен в фоновом режиме, значение токена передается в TokenContext:
<TokenContext.Provider value = {{ token }}>{children}</TokenContext.Provider>
Во время первого рендеринга будут выполняться следующие useEffect.
useEffect(() => {
// ...
}, [token]); // {}
Попробуйте добавить проверку, чтобы увидеть, является ли токен неопределенным/пустым объектом в useEffect?
useEffect(() => {
setIsLoading(true);
if (token && Object.keys(token).length !== 0){
// grab a pet
}
}, [token]);
Вместо того, чтобы писать
if (!pet) {
return (
isLoading ?? (
//Spinner renders here
)
);
Вы также можете использовать React.Suspense для отображения счетчика.
например
import { Suspense } from 'react';
//... other code
return (
<Suspense fallback = {<MySpinner />}>
<MyComponentToDisplayWhenPetIsFetched pet = {pet} />
</Suspense>
);
Этот урок объясняет Саспенс довольно хорошо
При обновлении контекста страницы данные будут потеряны, потому что они не хранятся в каком-либо хранилище постоянно. Вы можете хранить свой токен в памяти браузера (локальное хранилище или куки)