useEffect не вызывается и не обновляет состояние при получении api

Я получаю данные из API погоды с помощью хука useEffect и правильно объявляю зависимость. Мое состояние все еще не обновляется, и из-за этого я получаю ошибки в моей функции рендеринга. Я почти пробовал все, от избавления от массива зависимостей до объявления кратных в массиве зависимостей. Я не знаю, что не так с моей функцией. Ответ API JSON имеет следующий формат:

{
 location: {
 name: "Paris",
 region: "Ile-de-France",
 },
 current: {
  last_updated_epoch: 1564279222,
  last_updated: "2019-07-28 04:00",
  temp_c: 16,
  temp_f: 60.8,
  is_day: 0,
  condition: {
    text: "Clear",
    icon: "//cdn.apixu.com/weather/64x64/night/113.png",
    code: 1000
  },
  wind_mph: 6.9,
  wind_kph: 11.2
 }
}

и вот как выглядит мой код:

const Weather = ({ capital }) => {
  const [weather, setWeather] = useState(null);

  useEffect(() => {
    console.log("useEffect called");
    const getWeather = async () => {
      try {
        const res = await axios.get(
          `http://api.apixu.com/v1/current.json?key=53d601eb03d1412c9c004840192807&q=${capital}`
        );
        setWeather(res.data);
      } catch (e) {
        console.log(e);
      }
    };
    getWeather();
  }, [capital]);
  console.log(weather);

  return (
    <Card style={{ width: "18rem", marginTop: "25px" }}>
      <Card.Img variant="top" src={weather.current.condition.icon} />

      <Card.Header style={{ textAlign: "center", fontSize: "25px" }}>
        Weather in {capital}
      </Card.Header>
    </Card>
  )
}

Я ожидаю, что мне будет показано изображение значка, но я получаю это сообщение об ошибке в своей функции рендеринга:

TypeError: Cannot read property 'current' of null
Weather
src/components/Weather.js:26
  23 | 
  24 | return (
  25 |   <Card style={{ width: "18rem", marginTop: "25px" }}>
  26 |     <Card.Img variant="top" src={weather.current.condition.icon} />
     | ^  27 | 
  28 |     <Card.Header style={{ textAlign: "center", fontSize: "25px" }}>
  29 |       Weather in {capital}

и мой console.log(weather) возвращает значение null, исходное состояние, даже если оно вызывается после useEffect (), а console.log(useEffect called) вообще не регистрируется, что означает, что useEffect не вызывается.


person Navjeet Kaur    schedule 30.07.2019    source источник
comment
Удалите вашу зависимость [capital]   -  person Ru Chern Chong    schedule 30.07.2019
comment
Я попытался полностью удалить массив зависимостей, но это все еще не решает проблему, и я также пробовал использовать только [], но не работает. Однако мне нужно повторно отрендерить и обновить состояние, если капитал изменится, поэтому я точно знаю [], что это не ответ.   -  person Navjeet Kaur    schedule 30.07.2019


Ответы (3)


Сообщение об ошибке выдает это Cannot read property 'current' of null, единственное место, где вызывается current, находится в weather.current в src из Card.Img, поэтому мы делаем вывод, что weather было null во время рендеринга.

Причина, по которой это происходит, заключается в том, что вызов api является асинхронным, он не заполняет состояние сразу, поэтому сначала выполняется рендеринг и пытается прочитать .current из начального состояния погоды null.

Решение: в вашем render методе убедитесь, что не читаете weather.current, пока weather равно null.

Вы можете, например, использовать {weather && <Card>...</Card}, чтобы скрыть всю карточку, пока данные не будут загружены и не покажет индикатор загрузки, или вы можете использовать src={weather && weather.current.condition.icon} в качестве быстрого обходного пути.

const Weather = ({capital}) => {
  const [weather, setWeather] = useState(null);

  useEffect(() => {
    console.log("useEffect called");
    const getWeather = async () => {
      try {
        const res = await axios.get(
          `http://api.apixu.com/v1/current.json?key=53d601eb03d1412c9c004840192807&q=${capital}`,
        );
        setWeather(res.data);
      } catch (e) {
        console.log(e);
      }
    };
    getWeather();
  }, [capital]);
  console.log(weather);

  return (
    <Card style={{width: "18rem", marginTop: "25px"}}>
      <Card.Img variant="top" src={weather && weather.current.condition.icon} />

      <Card.Header style={{textAlign: "center", fontSize: "25px"}}>
        Weather in {capital}
      </Card.Header>
    </Card>
  );
};
person Moe    schedule 30.07.2019
comment
Ничего себе, это сработало. Даже console.log(weather) заполняется, и useEffect called также записывается в консоль. Я понимаю, почему мне пришлось проверять погоду в моей функции рендеринга, но почему мой console.log(weather) не записывал данные о погоде или мое состояние не обновлялось с моим setWeather()? - person Navjeet Kaur; 30.07.2019
comment
Причина, по которой это происходит, заключается в том, что вызов api является асинхронным, он не заполняет состояние сразу, поэтому сначала выполняется рендеринг и пытается прочитать .current из начального состояния null - person Moe; 30.07.2019

Итак, я столкнулся с аналогичной проблемой, когда казалось, что хук useEffect вообще не вызывается. Соответствующий фрагмент кода приведен ниже:

В моем функциональном компоненте это была последовательность появления кода:

  • объявление переменной const facetFields = [];, которое должно было быть установлено вызовом AJAX из useEffect хука

  • useEffect, в котором указанная выше переменная была установлена ​​с помощью AJAX.

     useEffect( ()=>{
          console.log('FacetTreeView: useEffect entered');
          facetFields = getFacetFieldNames(props.tabIndex);   
       }, []);
    
  • # P5 #
    # P6 #

С этим кодом консольный оператор внутри ловушки вообще не печатался. Кроме того, я продолжал получать неопределенную ошибку, когда map обрабатывал facetFields. Итак, оказывается, что useHook вызывается после рендеринга компонента. Итак, во время рендеринга переменная facetFields равна undefined. Итак, я исправил это, добавив эту строку в свою часть кода рендеринга JSX:

facetFields && facetFields.map(.....

После того, как я внес вышеупомянутое изменение, элемент управления начал переходить к ловушке. Итак, в итоге, если в JSX есть какой-либо код, который устанавливается из ловушки useEffect, то лучше всего отложить выполнение JSX до тех пор, пока выполнение ловушки не будет завершенный. Хотя это не обязательно, но это упростит чтение кода JSX. Этого можно достичь простым способом, используя флаг логического состояния.

  1. Определите состояние:

const [readyForRender, setReadyForRender] = React.useState (ложь); 2. Установить состояние в хуке.

useEffect( ()=>{
            console.log('FacetTreeView: useEffect entered');
            facetFields = getFacetFieldNames(props.tabIndex);
            setReadyForRender(true); }, []);
  1. Рендеринг JSX условно.

if (readyForRender) {return ({facetFields.map ((facetLabel, index) = ›{populateFacetInstances (facetLabel)})}); } else {return null; }

person Binita Bharati    schedule 01.10.2020

Однажды у меня была такая же загадочная проблема

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

<yourcomponent key="some_unique_value" />

Это связано с тем, что в большинстве случаев, когда ваш компонент повторно используется, в зависимости от способа его создания, он обычно может повторно отрисовать его с некоторыми изменениями вместо того, чтобы создавать его снова, когда вы повторно используете его. Следовательно, useEffect не будет вызываться . например, в SwitchRoute, циклах, условных выражениях ...

Таким образом, добавление ключа предотвратит это. Если он находится в цикле, вам нужно убедиться, что каждый элемент уникален, возможно, включив индекс i в ключ, если вы не можете найти лучший уникальный ключ.

person Abraham    schedule 07.07.2021