使用 Ionic/React/Typescript 传递道具时出错 - React.FC<'wrong props here'> 中的道具错误
Error when passing props with Ionic/React/Typescript - Wrong props in React.FC<'wrong props here'>
在解决了这个问题之后,我没有成功。我一直在检查这里的许多问题,但我找不到适合我的情况,所以拜托,我想向你寻求帮助。
我在通过 <WeatherCard/>
组件传递道具时遇到问题。测试应用程序有 2 个选项卡,并且两个选项卡都呈现 <WeatherCard/>
。
当我将 <WeatherCard/>
悬停在 Tab1.tsx 中时,Typescript 向我显示了错误的(意外的)道具 (WeatherProps),请参见下文:
(alias) const WeatherCard: React.FC<WeatherProps>
import WeatherCard
另一方面,当我将 <WeatherCard/>
悬停在 Tab2.tsx 时,它显示传递了正确的道具,见下文:
(alias) const WeatherCard: React.FC<WeatherCardProps>
import WeatherCard
此外,它显示以下错误:
[react-scripts] TS2322: 类型 '{ weatherIconUrl: string;天气数据:任何;天气变量:字符串[]; }' 不可分配给类型 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }'。
[react-scripts] 属性 'weatherIconUrl' 在类型 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }' 上不存在。
我 console.log 传入 <WeatherProperty/>
的道具,这些道具实际上是传递给 <WeatherCard/>
的道具所以,似乎出于某种原因,道具声明在
使用 Tab1
时不考虑 <WeatherProperty/>
我将包含以下代码:
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 class="ion-text-center">
<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;
您需要解决两件事:
- 从错误的组件中导入
Tab1.tsx
import WeatherCard from "../components/WeatherProperty";
// Maybe the correct one is "WeatherCard" ?
// import WeatherCard from "../components/WeatherCard";
- 为道具分配了错误的类型定义
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 }) => {
在解决了这个问题之后,我没有成功。我一直在检查这里的许多问题,但我找不到适合我的情况,所以拜托,我想向你寻求帮助。
我在通过 <WeatherCard/>
组件传递道具时遇到问题。测试应用程序有 2 个选项卡,并且两个选项卡都呈现 <WeatherCard/>
。
当我将 <WeatherCard/>
悬停在 Tab1.tsx 中时,Typescript 向我显示了错误的(意外的)道具 (WeatherProps),请参见下文:
(alias) const WeatherCard: React.FC<WeatherProps>
import WeatherCard
另一方面,当我将 <WeatherCard/>
悬停在 Tab2.tsx 时,它显示传递了正确的道具,见下文:
(alias) const WeatherCard: React.FC<WeatherCardProps>
import WeatherCard
此外,它显示以下错误:
[react-scripts] TS2322: 类型 '{ weatherIconUrl: string;天气数据:任何;天气变量:字符串[]; }' 不可分配给类型 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }'。 [react-scripts] 属性 'weatherIconUrl' 在类型 'IntrinsicAttributes & WeatherProps & { children?: ReactNode; }' 上不存在。
我 console.log 传入 <WeatherProperty/>
的道具,这些道具实际上是传递给 <WeatherCard/>
的道具所以,似乎出于某种原因,道具声明在
使用 Tab1
<WeatherProperty/>
我将包含以下代码:
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 class="ion-text-center">
<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;
您需要解决两件事:
- 从错误的组件中导入
Tab1.tsx
import WeatherCard from "../components/WeatherProperty";
// Maybe the correct one is "WeatherCard" ?
// import WeatherCard from "../components/WeatherCard";
- 为道具分配了错误的类型定义
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 }) => {