Home > Software engineering >  Error when passing props with Ionic/React/Typescript - Wrong props in React.FC<'wrong props
Error when passing props with Ionic/React/Typescript - Wrong props in React.FC<'wrong props

Time:03-06

After dealing with this problem for good while I got no success. I have been checking many questions around here but I could not find one that suits my case, so please, I would like to ask you for help.

I have a problem passing my props through the <WeatherCard/> component. The test application has 2 tabs and both tabs have the <WeatherCard/> being rendered. When I hover <WeatherCard/> in Tab1.tsx Typescript shows me the wrong (unexpected) props (WeatherProps), see below:

     (alias) const WeatherCard: React.FC<WeatherProps>
import WeatherCard

On the other hand, whereas when I hover <WeatherCard/> in Tab2.tsx it shows the correct props being passed, see below:

         (alias) const WeatherCard: React.FC<WeatherCardProps>
 import WeatherCard

Moreover it shows the following error:

[react-scripts] TS2322: Type '{ weatherIconUrl: string; weatherData: any; weatherVars: string[]; }' is not assignable to type 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }'. [react-scripts] Property 'weatherIconUrl' does not exist on type 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }'.

I console.log the props passed in <WeatherProperty/> and these props are actually the props passed to <WeatherCard/> so, it seems like for some reason the props declared in <WeatherProperty/> are not taken into account when using Tab1

I will include the code below:

Tab1.tsx

import {
  IonButton,
  IonButtons,
  IonCard,
  IonCardContent,
  IonCol,
  IonContent,
  IonGrid,
  IonHeader,
  IonItem,
  IonPage,
  IonRow,
  IonText,
  IonThumbnail,
  IonTitle,
  IonToolbar,
  IonLoading,
  IonAlert,
} from "@ionic/react";
import styles from "./Tab1.module.css";
import { RefreshOutline } from "react-ionicons";
import { Geolocation } from "@capacitor/geolocation";
import React, { useEffect } from "react";
import { RootState } from "../app/store";
import { useDispatch, useSelector } from "react-redux";
import { useGetCurrentPositionWeatherQuery } from "../services/weather";
import { setQueryCoord } from "../app/coordQuerySlice";
import TestSkeleton from "../components/TestSkeleton";
import WeatherCard from "../components/WeatherProperty";

const Tab1: React.FC = () => {
  const dispatch = useDispatch();
  const coordQueryState = useSelector((state: RootState) => state.coordQuery);
  const {
    refetch,
    data: weatherData,
    isFetching,
    isError,
  } = useGetCurrentPositionWeatherQuery(
    {
      lat: coordQueryState.lat,
      lon: coordQueryState.lon,
      appid: "cd555b96865912ac5781d36d6d7de140",
      units: "metric",
    },
    { skip: coordQueryState.skip }
  );

  const setCurrentPosition = async () => {
    const data = await Geolocation.getCurrentPosition();
    const {
      coords: { latitude: latFetched },
      coords: { longitude: lonFetched },
    } = data;
    dispatch(setQueryCoord({ lat: latFetched, lon: lonFetched, skip: false }));
  };

  useEffect(() => {
    setCurrentPosition();
  }, []);

  function refreshCurrentPositionHandler() {
    refetch();
  }

  if (weatherData) {
    console.log(weatherData);
  }

  const weatherIconUrl = weatherData
    ? `https://openweathermap.org/img/wn/${weatherData.weather[0].icon}@2x.png`
    : undefined;
  const weatherVars = weatherData ? Object.keys(weatherData.main) : undefined;

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar className={styles["ion-toolbar-dashboard"]}>
          <IonTitle className="ion-margin-bottom" size="large">
            Dashboard
          </IonTitle>
          <IonButtons slot="end">
            <IonButton>
              <RefreshOutline
                onClick={refreshCurrentPositionHandler}
                color={"black"}
                height="35px"
                width="35px"
                cssClasses={styles.refreshOutline}
              />
            </IonButton>
          </IonButtons>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        <IonGrid className="ion-no-margin">
          <IonRow style={{ margin: "10px" }}>
            <IonCol className="ion-text-center">
              <h1 style={{ fontSize: "20px" }}>
                Here's your location based weather
              </h1>
            </IonCol>
          </IonRow>
        </IonGrid>
        {!weatherData && <IonLoading isOpen={!weatherData} />}
        {isFetching && !weatherData && <IonLoading isOpen={isFetching} />}
        {isError && <IonAlert isOpen={isError} />}
        {!isFetching && weatherData && (
          <WeatherCard
            weatherIconUrl={weatherIconUrl as string} \\problem is indeed here
            weatherData={weatherData}
            weatherVars={weatherVars as string[]}
          />
        )}
      </IonContent>
    </IonPage>
  );
};

export default Tab1;

Tab2.tsx

import {
  IonContent,
  IonHeader,
  IonPage,
  IonTitle,
  IonToolbar,
} from "@ionic/react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setQueryCity } from "../app/cityQuerySlice";
import { RootState } from "../app/store";
import WeatherCard from "../components/WeatherCard";
import { useGetWeatherByCityQuery } from "../services/weather";
import styles from "./Tab1.module.css";

const Tab2: React.FC = () => {
  const dispatch = useDispatch();
  const cityQueryState = useSelector((state: RootState) => state.cityQuery);
  const {
    refetch,
    data: weatherData,
    isFetching,
    isError,
  } = useGetWeatherByCityQuery(
    {
      q: cityQueryState.city,
      appid: "cd555b96865912ac5781d36d6d7de140",
      units: "metric",
    },
    { skip: cityQueryState.skip }
  );

  useEffect(() => {
    dispatch(setQueryCity({ city: "London", skip: false }));
  }, [dispatch]);

  const weatherIconUrl = weatherData
    ? `https://openweathermap.org/img/wn/${weatherData.weather[0].icon}@2x.png`
    : undefined;
  const weatherVars = weatherData ? Object.keys(weatherData.main) : undefined;

  return (
    <IonPage>
      <IonHeader>
        <IonToolbar className={styles["ion-toolbar-dashboard"]}>
          <IonTitle className="ion-margin-bottom" size="large">
            Search
          </IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        {!weatherData && <p>weather data is loading</p>}
        {weatherData && (
          <WeatherCard
            weatherData={weatherData} \\ and here the props are passed properly...
            weatherIconUrl={weatherIconUrl as string}
            weatherVars={weatherVars as string[]}
          />
        )}
      </IonContent>
    </IonPage>
  );
};

export default Tab2;

WeatherCard.tsx

import {
  IonCard,
  IonCardContent,
  IonGrid,
  IonRow,
  IonCol,
  IonText,
} from "@ionic/react";
import { useEffect } from "react";
import WeatherProperty from "./WeatherProperty";

interface WeatherCardProps {
  weatherIconUrl: string;
  weatherVars: string[];
  weatherData: any;
}

const WeatherCard: React.FC<WeatherCardProps> = (props: WeatherCardProps) => {
  const { weatherVars, weatherData, weatherIconUrl } = props;

  console.log(weatherData);
  console.log(weatherData);
  console.log(weatherIconUrl);

  useEffect(() => {
    console.log("component WeatherCard mounted");
  });

  return (
    <IonCard>
      <IonCardContent>
        <IonGrid>
          <IonRow>
            <IonCol >
              <IonText color="primary">
                <h1 style={{ marginBottom: "0px", display: "inline" }}>
                  {weatherData.name},
                  <span style={{ color: "grey" }}>
                    {" "}
                    {weatherData.sys.country === "JP"
                      ? "Japan"
                      : "Outside Japan"}
                  </span>
                </h1>
              </IonText>
            </IonCol>
          </IonRow>
          <IonRow className="ion-justify-content-center ion-align-items-center">
            <IonCol size="auto">
              <img
                style={{
                  width: "70px",
                  height: "70px",
                  display: "inline",
                }}
                src={weatherIconUrl}
              />
            </IonCol>
            <IonCol size="auto" className="ion-justify-content-center">
              <h1
                style={{
                  display: "inline",
                  fontSize: "18px",
                }}
              >
                {weatherData.weather[0].description}
              </h1>
            </IonCol>
          </IonRow>
          <IonRow>
            <IonCol>
              <WeatherProperty
                main={weatherVars[1]}
                mainValue={weatherData.main[weatherVars[1]]}
              />
            </IonCol>
            <IonCol>
              <WeatherProperty
                main={weatherVars[2]}
                mainValue={weatherData.main[weatherVars[2]]}
              />
            </IonCol>
          </IonRow>
          <IonRow>
            <IonCol>
              <WeatherProperty
                main={weatherVars[3]}
                mainValue={weatherData.main[weatherVars[3]]}
              />
            </IonCol>
            <IonCol>
              <WeatherProperty
                main={weatherVars[5]}
                mainValue={weatherData.main[weatherVars[5]]}
              />
            </IonCol>
          </IonRow>
        </IonGrid>
      </IonCardContent>
    </IonCard>
  );
};

export default WeatherCard;

WeatherProperty.tsx

import { IonItem, IonLabel, IonText, IonThumbnail } from "@ionic/react";
import { useEffect } from "react";
import "./WeatherProperty.module.css";

interface WeatherProps {
  main: string;
  mainValue: number;
}

const WeatherProperty: React.FC<WeatherProps> = (props: WeatherProps) => {
  console.log(`these are the props in WeatherProperty component`);
  console.log(props);

  let propMod;
  let unit;
  let fileName;
  if (props.main === "feels_like") {
    propMod = "It feels like:";
    unit = "deg";
    fileName = "feels-like.png";
  }
  if (props.main === "temp_min") {
    propMod = "Minimum temperature:";
    unit = "deg";
    fileName = "temp-min.png";
  }
  if (props.main === "temp_max") {
    propMod = "Maximum temperature:";
    unit = "deg";
    fileName = "temp-max.png";
  }
  if (props.main === "humidity") {
    propMod = "Humidity:";
    unit = "%";
    fileName = "humidity.png";
  }
  useEffect(() => {
    console.log("component WeatherProperty mounted");
  });

  return (
    <IonItem lines="none">
      <IonThumbnail slot="start" className="weather-icons">
        <img src={`${window.location.origin}/assets/${fileName}`} />
      </IonThumbnail>
      <IonLabel slot="">
        <IonText color="primary">
          <h1 style={{ fontSize: "15px" }}>{propMod}</h1>
        </IonText>
        <h2>{`${Math.trunc(props.mainValue)} ${unit}`}</h2>
      </IonLabel>
    </IonItem>
  );
};

export default WeatherProperty;

CodePudding user response:

There are two things you need to fix:

  1. Imported from the wrong component

Tab1.tsx

import WeatherCard from "../components/WeatherProperty";

// Maybe the correct one is "WeatherCard" ?
// import WeatherCard from "../components/WeatherCard";
  1. Assigned the wrong type definition for props

WeatherCard.tsx

const WeatherProperty: React.FC<WeatherProps> = (props: WeatherProps) => {

// you can just omit the prop's type 
// (props) => { 
// 
// or use object destructuring for the convenience
// ({ weatherData, weatherVar, weatherIconUrl }) => {
  • Related