score:16

Accepted answer

The error message gives it away, Cannot read property 'current' of null, the only place where current is called is in weather.current in the src of Card.Img, so we deduce that weather was null during the render.

The reason this happens is because the api call is asynchronus, it doesn't populate the state immediately, so the render happens first and tries to read .current from the initial weather state null.

Solution: in your render method, make sure not to read weather.current while weather is null.

You can for example use {weather && <Card>...</Card} to hide the whole card until the data is loaded and show a loading indicator, or you can use src={weather && weather.current.condition.icon} as a quick workaround.

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>
  );
};

score:2

So, I was facing a similar issue, where it seemed like useEffect hook was not getting invoked at all. The concerned code snippet is given below:

Within my functional component, this was the sequence of the appearance of the code:

  • a variable const facetFields = []; declaration which was supposed to be set by a AJAX call from within useEffect hook

  • useEffect hook within which the above variable was getting set with AJAX.

     useEffect( ()=>{
          console.log('FacetTreeView: useEffect entered');
          facetFields = getFacetFieldNames(props.tabIndex);   
       }, []);
    
  • JSX code that uses the variable.

    return (<MyComponent> { facetFields.map((facetLabel, index) => { populateFacetInstances(facetLabel) }) } </Component>)

With this code, the console statement inside the hook was not printing at all. Also, I kept getting a undefined error while map'ing the facetFields. So, it turns out that the useHook is called after the component is rendered. So, during the rendering, the variable facetFields is undefined.So, I fixed this by adding this line to my JSX rendering part of code:

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

Once, I made the above change, the control started going to the hook.So, in summary, if there is any code within JSX that is being set from useEffect hook, then its best to defer the execution of the JSX till hook execution is completed. Although, this is not mandatory, but it will make the JSX code easier to read. This can be achieved in a simple way by using a boolean state flag.

  1. Define the state:

const [readyForRender, setReadyForRender] = React.useState(false); 2. Set state in hook.

useEffect( ()=>{
            console.log('FacetTreeView: useEffect entered');
            facetFields = getFacetFieldNames(props.tabIndex);
            setReadyForRender(true); }, []);
  1. Render JSX conditionally.

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

score:2

I had the same puzzling issue one time

You can try adding a key prop on the component when it is created in the parent code

<yourcomponent key="some_unique_value" />

This is because in most cases, when your component is reused, based on the way it is created, it may usually re-render it with some changes instead of creating it again when you reuse it, Hence the useEffect is not going to be called. eg in SwitchRoute, loops, conditionals...

So adding a key will prevent this from happening. If it is in a loop, you need to make sure each element is unique, maybe by including the index i in the key if you can't find any better unique key.


Related Query

More Query from same tag