Обещайте данные при использовании react и axios

#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(...) , будет выполнен до завершения запроса.

Чтобы надлежащим образом справиться с этой ситуацией, выполните следующие действия:

  1. Создайте состояние, которое указывает, находится ли HTTP-запрос в ожидании или нет. Его начальное значение должно быть истинным
     const [isPending, setIsPending] = useState(true);
     
  2. Используйте 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-запрос будет инициирован только один раз, после первоначального рендеринга компонента

  3. Пока запрос находится в ожидании, покажите пользователю какой-нибудь загрузочный счетчик. Когда компонент впервые отобразится, как 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>
}
 

В дополнение к тому, что у вас было, теперь после завершения запроса устанавливается состояние, только после этого мы можем решить, вошел ли пользователь в систему или нет, в противном случае мы перенаправим пользователя еще до выполнения запроса, так как по умолчанию состояние инициализируется как ложное

Пустой массив при использовании эффекта используется для запуска кода только при монтировании компонента