javascript 提取API时,useEffect未被调用且未更新状态

lhcgjxsq  于 2023-04-10  发布在  Java
关注(0)|答案(4)|浏览(120)

我使用useEffect钩子从天气API获取数据,并正确声明依赖项。我的状态仍然没有更新,因此我在渲染函数中出错。我几乎尝试了从摆脱依赖数组到在依赖数组中声明倍数的所有方法。我不知道我的函数有什么问题。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没有被调用。

w8ntj3qf

w8ntj3qf1#

错误消息泄露了它,Cannot read property 'current' of nullcurrent被调用的唯一地方是在Card.Imgsrc中的weather.current中,因此我们推断在渲染期间weathernull
发生这种情况的原因是因为API调用是异步的,它不会立即填充状态,所以渲染首先发生,并尝试从初始天气状态null读取.current
解决方案:在render方法中,确保在weathernull时不读取weather.current
例如,您可以使用{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>
  );
};
h9a6wy2h

h9a6wy2h2#

我有一次也遇到过同样的难题
您可以尝试在父代码中创建组件时在组件上添加键 prop

<yourcomponent key="some_unique_value" />

这是因为在大多数情况下,当你的组件被重用时,基于它的创建方式,它通常会重新渲染它,而不是在重用它时再次创建它,因此useEffect不会被调用。
所以添加一个键可以防止这种情况发生。如果是在循环中,你需要确保每个元素都是唯一的,如果你找不到更好的唯一键,可以在键中包含索引i

8hhllhi2

8hhllhi23#

所以,我也遇到了类似的问题,似乎useEffect钩子根本没有被调用。下面给出了相关的代码片段:
在我的函数组件中,这是代码的外观序列:

  • 一个变量const facetFields = [];声明,应该由useEffect钩子中的 AJAX 调用设置
  • useEffect钩子,上面的变量是用 AJAX 设置的。
useEffect( ()=>{
      console.log('FacetTreeView: useEffect entered');
      facetFields = getFacetFieldNames(props.tabIndex);   
   }, []);
  • 使用变量的JSX代码。

return( {facetFields.map((面标签,索引)=〉{填充面示例(面标签)})})
使用这段代码,钩子中的控制台语句根本没有打印。此外,我在map 'ing facetFields时一直得到一个未定义的错误。所以,原来useHook是在组件渲染后调用的。所以,在渲染期间,变量facetFieldsundefined。所以,我通过将这一行添加到我的JSX渲染代码部分来修复这个问题:

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

一旦,我做了上面的更改,控件开始进入钩子。所以,总而言之,如果JSX中有任何代码是从useEffect钩子设置的,那么最好推迟JSX的执行,直到钩子执行完成。虽然,这不是强制性的,但它会使JSX代码更容易阅读。这可以通过使用布尔状态标志以一种简单的方式实现。
1.定义状态:
const [readyForRender,setReadyForRender] = React.useState(false);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;}

4szc88ey

4szc88ey4#

我有类似的问题。它有时工作,大多数时候它不工作。我已经尝试根据上面建议的解决方案更新代码,但没有运气。我在这里尝试将从EDIdata.json接收到的数据传递到子组件。但首先,由于页面加载时没有调用getData(),因此没有从API获取数据。

import { React, useEffect, useState } from 'react';
import MilestoneSummary from './MilestoneSummary';
import { Grid, Column } from '@carbon/react';

const OrderSummary = () => {
    const [data, setData] = useState({ data: {} });
    useEffect(() => {
        getData()
    })

    const getData = () => {
        fetch('EDIData.json'
            , {
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                }
            }
        )
            .then(function (response) {
                console.log("JSON response" + response)
                return response.json();
            })
            .then(function (myJson) {
                setData(myJson)
            })
            .catch(err => { console.log(err) })
    }

    console.log(data);
    return (
        <>
            <Grid>
                <Column md={4} lg={4} sm={4}>
                    <h3 className="landing-page__label">Order milestone summary</h3>
                    <MilestoneSummary summaryItems={data.OrderMilestoneSummary} />
                </Column>
            </Grid>
        </>
    )
};

export default OrderSummary;

数据的console.log不记录任何东西。请注意。

相关问题