繁体   English   中英

无法在 useEffect 中访问 state

[英]Can't access state inside useEffect

看来我无法从项目中的 useEffect() 挂钩访问 state。 更具体地说,我成功地从 API 中获取了一些数据; 然后我将这些数据保存在组件的 state 中; 然后它的一部分在上下文中“发布”,其余部分作为道具传递给子组件。 问题是 setState() 函数没有正确更新 state; 我在调试时注意到,如果我监视 state 变量,它们会显示为null ,即使承诺确实实现并且 JS 成功地将正确的数据分配给服务变量resMainCityCallresUrlAltCity1CallresUrlAltCity2Call 钩子 useState() 将null作为默认值分配给 state 变量mainCityDataaltCity1DataaltCity2Data ,但是 state setter 函数无法将获取的值分配给 state,它保持设置为8863418 5391

调用者.js

import React from 'react';
import { useState, useEffect } from 'react';
import { MainCity } from './MainCity';
import { AltCity } from './AltCity';


export const MainCityContext = React.createContext(
    null // context initial value. Let's fetch weather data, making our Caller component the provider. The main city box and the other two boxes will be consumers of this context, aka the data fetched.
);


export const Caller = () =>
{
    const [mainCityData, setMainCityData] = useState(null);
    const [altCity1Data, setAltCity1Data] = useState(null);
    const [altCity2Data, setAltCity2Data] = useState(null);

    useEffect(() =>
        {
            const fetchData = async () =>
            {
                const urlMainCityBox = "https://api.openweathermap.org/data/2.5/weather?lat=45.0677551&lon=7.6824892&units=metric&appid=65e03b16f8eb6ba0ef7776cd809a50cd";
                // const urlTodayBox = "https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=45.0677551&lon=7.6824892&appid=65e03b16f8eb6ba0ef7776cd809a50cd";
                // const urlWeekMonthBox = "https://api.openweathermap.org/data/2.5/forecast/daily?lat=45.0677551&lon=7.6824892&cnt=7&appid=65e03b16f8eb6ba0ef7776cd809a50cd";
                const urlAltCity1 = "https://api.openweathermap.org/data/2.5/weather?lat=51.5073219&lon=-0.1276474&units=metric&appid=65e03b16f8eb6ba0ef7776cd809a50cd";
                const urlAltCity2 = "https://api.openweathermap.org/data/2.5/weather?lat=41.8933203&lon=12.4829321&units=metric&appid=65e03b16f8eb6ba0ef7776cd809a50cd";


                let globalResponse = await Promise.all([
                    fetch(urlMainCityBox),
                    //fetch(urlTodayBox),
                    //fetch(urlWeekMonthBox),
                    fetch(urlAltCity1),
                    fetch(urlAltCity2)
                ]);

                const resMainCityCall = await globalResponse[0].json();
                // const resUrlTodayBoxCall = await globalResponse[1].json();
                // const resUrlWeekMonthBoxCall = await globalResponse[2].json();
                const resUrlAltCity1Call = await globalResponse[1].json();
                const resUrlAltCity2Call = await globalResponse[2].json();

                setMainCityData({
                    "name" : resMainCityCall.name,
                    "weather" : resMainCityCall.weather[0].main,
                    "weather_description" : resMainCityCall.weather[0].description,
                    "icon" : resMainCityCall.weather[0].icon,
                    "temperature" : resMainCityCall.weather[0].main.temp,
                    "time" : convertTimeOffsetToDate( resMainCityCall.timezone )
                    // spot for the forecasted data (paid API on OpenWeather).

                });

                setAltCity1Data({
                    "name" : resUrlAltCity1Call.name,
                    "weather" : resUrlAltCity1Call.weather[0].main,
                    "weather_description" : resUrlAltCity1Call.weather[0].description,
                    "icon" : resUrlAltCity1Call.weather[0].icon,
                    "temperature" : resUrlAltCity1Call.weather[0].main.temp,
                    "time" : convertTimeOffsetToDate( resUrlAltCity1Call.timezone )  // time attribute is type Date
                });

                setAltCity2Data({
                    "name" : resUrlAltCity2Call.name,
                    "weather" : resUrlAltCity2Call.weather[0].main,
                    "weather_description" : resUrlAltCity2Call.weather[0].description,
                    "icon" : resUrlAltCity2Call.weather[0].icon,
                    "temperature" : resUrlAltCity2Call.weather[0].main.temp,
                    "time" : convertTimeOffsetToDate( resUrlAltCity2Call.timezone )  // time attribute is type Date
                });

                console.log("Status updated.");
                console.log(mainCityData);
                console.log(altCity1Data);
                console.log(altCity2Data);

            }

            fetchData().catch((error) => { console.log("There was an error: " + error)});

        }, []); // useEffect triggers only after mounting phase for now.


        let mainCityComponent, altCity1Component, altCity2Component = null;
        // spot left for declaring the spinner and setting it on by default.
        if ((mainCityData && altCity1Data && altCity2Data) != null)
        {
            mainCityComponent = <MainCity />;  // Not passing props to it, because MainCity has nested components that need to access data. It's made available for them in an appropriate Context.
            altCity1Component = <AltCity data={ altCity1Data } />
            altCity2Component = <AltCity data={ altCity2Data } />
        }
        // spot left for setting the spinner off in case the state is not null (data is fetched).

    
    return (
        <div id="total_render">
            <MainCityContext.Provider value={ mainCityData } >
                { mainCityComponent }
            </MainCityContext.Provider>
            { altCity1Component }
            { altCity2Component }
        </div>
        
    );

}

const convertTimeOffsetToDate = (secondsFromUTC) =>
{
    let convertedDate = new Date();  // Instanciating a Date object with the current UTC time.
    convertedDate.setSeconds(convertedDate.getSeconds() + secondsFromUTC);

    return convertedDate;
}

由于 state 保持null ,检查

if ((mainCityData && altCity1Data && altCity2Data) != null)

没有通过,没有任何东西被渲染。

您的代码和您的逻辑可能存在一些问题。 我会在这里列出它们并提出问题,以便您可以考虑它们并以您认为有意义的最佳方式回答。

useEffect()中的逻辑太多

作为建议,您可以保存请求的结果并将其保存在 state 中。当 state 更新时,您更新相应的状态。 这样的事情可以工作:

const [requestResponseValue, setRequestReponseValue] = useState([]);
const [first, setFirst] = useState();
const [second, setSecond] = useState();
const [last, setLast] = useState();

useEffect(() => {
  const initialFetch = async () => {
    const allResult = await Promise.all([
      fetchFirst(),
      fetchSecond(),
      fetchLast(),
    ]);
    setRequestReponseValue(allResult);
  }

  initialFetch();
}, []);

useEffect(() => {
  if (requestResponseValue[0] !== undefined) {
    setFirst(requestResponseValue[0]);
  }
}, [requestResponseValue, setFirst]);

useEffect(() => {
  if (requestResponseValue[1] !== undefined) {
    setSecond(requestResponseValue[1]);
  }
}, [requestResponseValue, setSecond]);

useEffect(() => {
  if (requestResponseValue[2] !== undefined) {
    setLast(requestResponseValue[2]);
  }
}, [requestResponseValue, setLast]);

更新后立即记录状态

当您调用setX() function 时,相应的 state 不会在您调用该 function 后立即更改。此代码永远不会记录正确的值:

const [age, setAge] = useState(20);
useEffect(() => {
  setAge(30);
  console.log(age);
}, []);

它会记录20而不是30

设置子组件的困惑

这段代码:

let mainCityComponent, altCity1Component, altCity2Component = null;

从以下位置设置值:

  • mainCityComponent undefined
  • altCity1Componentundefined
  • altCity2Componentnull

那是你要的吗?

检查逻辑操作的困惑

另外,这段代码:

if ((mainCityData && altCity1Data && altCity2Data) != null)

使用!=运算符检查(mainCityData && altCity1Data && altCity2Data)是否与null不同。 如果你想检查每一个,你应该写这样的东西:

if (
  mainCityData !== null
  && altCity1Data !== null
  && altCity2Data !== null
)

但是那个意思和你写的完全不一样。 另外,我不能肯定地说,但我认为那些“组件”变量永远不会呈现,因为组件 API 不希望它们发生变化,也不会“等待”它们。 长话短说:它们是不可改变的。 (但我不是 100% 确定这一点,因为我没有具体测试过)

即使这样,您也可以使用不同的方法来编写它。

使用!! 和条件渲染

为了避免这种特殊情况,您可以像这样编写渲染 function:

return (
  <div id="total_render">
    <MainCityContext.Provider value={ mainCityData } >
      {!!mainCityData ? <MainCity /> : null}
    </MainCityContext.Provider>
    {!!altCity1Data ? <AltCity alt={altCity1Data} /> : null}
    {!!altCity2Data ? <AltCity alt={altCity2Data} /> : null}
  </div>
);

这样,当mainCityDataaltCity1DataaltCity2Data状态发生变化时,它们将为您重新呈现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM