【问题标题】:useEffect not being called and not updating state when api is fetched获取 api 时未调用 useEffect 且未更新状态
【发布时间】:2019-12-07 08:46:25
【问题描述】:

我正在使用 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 没有被调用。

【问题讨论】:

  • 移除你的依赖[capital]
  • 我已经尝试完全删除依赖数组,但它仍然不能解决问题,我也尝试过仅使用 [] 但不起作用。但是,如果资本发生变化,我确实需要重新渲染和更新状态,所以我确定[] 这不是答案。

标签: javascript reactjs react-hooks use-effect


【解决方案1】:

错误信息泄露了Cannot read property 'current' of null,唯一调用current的地方是在Card.Imgsrc中的weather.current,所以我们推断weathernull期间渲染。

发生这种情况的原因是因为 api 调用是异步的,它不会立即填充状态,所以渲染首先发生并尝试从初始天气状态 null 读取 .current

解决方案:在您的render 方法中,确保不要读取weather.currentweathernull

例如,您可以使用{weather &amp;&amp; &lt;Card&gt;...&lt;/Card} 隐藏整个卡片,直到数据加载并显示加载指示器,或者您可以使用src={weather &amp;&amp; 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>
  );
};

【讨论】:

  • 哇,好用。甚至console.log(weather) 正在填充并且useEffect called 也被记录到控制台。我明白为什么我必须在渲染函数中检查天气,但为什么我的 console.log(weather) 没有记录天气数据,或者我的状态没有用我的 setWeather() 更新?
  • 发生这种情况的原因是因为api调用是异步的,它不会立即填充状态,所以渲染首先发生并尝试从初始状态null读取.current跨度>
【解决方案2】:

所以,我遇到了类似的问题,似乎 useEffect 钩子根本没有被调用。相关代码sn-p如下:

在我的功能组件中,这是代码出现的顺序:

  • 一个变量 const facetFields = []; 声明应该由 useEffect 钩子中的 AJAX 调用设置

  • useEffect 使用 AJAX 设置上述变量的钩子。

     useEffect( ()=>{
          console.log('FacetTreeView: useEffect entered');
          facetFields = getFacetFieldNames(props.tabIndex);   
       }, []);
    
  • 使用该变量的 JSX 代码。

    return ( { facetFields.map((facetLabel, index) => { populateFacetInstances(facetLabel) }) } )

使用此代码,钩子内的控制台语句根本不会打印。此外,在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。

如果(准备好渲染){ return ( { facetFields.map((facetLabel, index) => { populateFacetInstances(facetLabel) }) } ); } 别的{ 返回空值; }

【讨论】:

    【解决方案3】:

    我曾经遇到过同样令人费解的问题

    您可以尝试在父代码中创建组件时在组件上添加key prop

    <yourcomponent key="some_unique_value" />
    

    这是因为在大多数情况下,当您的组件被重用时,根据它的创建方式,它通常可能会重新渲染它并进行一些更改,而不是在您重用它时再次创建它,因此 useEffect 是不会被调用。例如在 SwitchRoute、循环、条件...

    所以添加一个密钥可以防止这种情况发生。如果它在循环中,则需要确保每个元素都是唯一的,如果找不到更好的唯一键,可以在键中包含索引 i

    【讨论】:

      猜你喜欢
      • 2022-01-22
      • 2021-05-18
      • 2020-10-26
      • 2021-02-14
      • 2023-03-07
      • 1970-01-01
      • 2020-06-10
      • 2022-01-08
      • 1970-01-01
      相关资源
      最近更新 更多