#reactjs #asynchronous #async-await #promise #axios
Вопрос:
в этом коде всякий раз, когда я пытаюсь перейти на /панель мониторинга, он не будет ждать ответа axios и сразу же переходит к возвращению части, и он использует loggedin
значение по умолчанию, которое находится undefined
здесь. Ну, я думаю, мне следует использовать обещания, но я не знаю, как это сделать… Поэтому я был бы признателен, если бы кто-нибудь мог помочь.
import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Redirect } from 'react-router';
import OverallCondition from "./dashOverall";
import Toolbar from "./dashToolbar";
export default function Dashboard(){
const [loggedin, check] = useState()
axios.get('/loggedin')
.then(response => {
check(response.data)
})
.catch(err => console.error(err));
return <section className='dashboard'>
{loggedin ? <div>
<Toolbar/>
<OverallCondition/>
</div> : <Redirect to='/login'/>}
</section>
}```
Комментарии:
1. Вы должны сделать этот маршрут защищенным, перед отображением этого компонента проверьте, вошел ли пользователь в систему или нет. Вы проверяете, вошел пользователь в систему или нет во время рендеринга этого компонента, что само по себе является проблемой.
Ответ №1:
Вам нужно использовать useEffect
hook для выполнения HTTP-запроса.
Выполнение HTTP-запроса на верхнем уровне внутри вашего функционального компонента будет инициировать новый HTTP-запрос каждый раз, когда ваш компонент повторно отображается.
Кроме того, поскольку axios.get(...)
это асинхронный код, приведенный ниже axios.get(...)
, будет выполнен до завершения запроса.
Чтобы надлежащим образом справиться с этой ситуацией, выполните следующие действия:
- Создайте состояние, которое указывает, находится ли HTTP-запрос в ожидании или нет. Его начальное значение должно быть истинным
const [isPending, setIsPending] = useState(true);
- Используйте
useEffect
крюк, чтобы сделать HTTP-запрос. Выполнение HTTP-запросов на верхнем уровне внутри компонента функции не является правильным подходом — вы не хотите делать запрос каждый раз, когда ваш компонент повторно отображаетсяТакже не забудьте установить
isPending
значениеfalse
, иначе пользователь будет продолжать видеть счетчик загрузки даже после завершения запроса. Вы можете использовать этотfinally()
метод для вызоваsetIsPending(false)
useEffect(() => { axios.get('/loggedin') .then(response => setLoggedIn(response.data)) .catch(err => console.error(err)) .finally(() => setIsPending(false)); }, []);
Пустой массив, передаваемый в качестве второго аргумента в
useEffect
хук, гарантирует, что HTTP-запрос будет инициирован только один раз, после первоначального рендеринга компонента - Пока запрос находится в ожидании, покажите пользователю какой-нибудь загрузочный счетчик. Когда компонент впервые отобразится, как
isPending
это верно, пользователь увидит счетчик загрузкиif (isPending) { return <Spinner/>; // you need to create the "Spinner" component }
Вот как вы могли бы реализовать описанные выше шаги в своем коде:
function Dashboard() {
const [loggedin, setLoggedIn] = useState();
// this state will be used to show a loading spinner
const [isPending, setIsPending] = useState(true);
// for HTTP request, use the "useEffect" hook
useEffect(() => {
axios.get('/loggedin')
.then(response => setLoggedIn(response.data))
.catch(err => console.error(err))
.finally(() => setIsPending(false));
}, []);
// show a spinner to the user while HTTP
// request is pending
if (isPending) {
return <Spinner/>;
}
return (
<section className='dashboard'>
{loggedin ? (
<div>
<Toolbar/>
<OverallCondition/>
</div>
) : <Redirect to='/login'/>
}
</section>
);
}
Ответ №2:
Проблемы с кодом
Используйте лучшую терминологию с возвратом useState
Вместо
const [loggedin, check] = useState()
Воспользуйся
const [loggedin, setLoggedin] = useState()
Понять жизненный цикл
Ваш axios.get находится внутри тела функции, весь код там будет выполняться при каждом рендеринге, это, конечно, не то, что вы хотели, для операций, которые могут иметь побочные эффекты, вам нужно использовать крючок useEffect https://reactjs.org/docs/hooks-effect.html
Крючок useEffect позволяет вам лучше контролировать, когда будет выполняться код, в вашем случае вы хотите, чтобы он запускался один раз
const ShowOnceLoaded = ({ isLoaded, children }) => {
if (!isLoaded)
return <p>Loading...</p>
return <>{children}</>
}
export default function Dashboard(){
const [ isLoaded, setIsLoaded ] = useState(false)
const [loggedin, setLoggedin] = useState(false)
React.useEffect(() => {
axios.get('/loggedin')
.then(response => {
setLoggedin(true)
setIsLoaded(true)
})
.catch(err => console.error(err));
}, [])
return <section className='dashboard'>
<ShowOnceLoaded isLoaded={isLoaded}>
{loggedin ? <div>
<Toolbar/>
<OverallCondition/>
</div> : <Redirect to='/login'/>}
</ShowOnceLoaded>
</section>
}
В дополнение к тому, что у вас было, теперь после завершения запроса устанавливается состояние, только после этого мы можем решить, вошел ли пользователь в систему или нет, в противном случае мы перенаправим пользователя еще до выполнения запроса, так как по умолчанию состояние инициализируется как ложное
Пустой массив при использовании эффекта используется для запуска кода только при монтировании компонента