Material UI 自动完成的打字稿平等问题
Typescript Equality issue on Material UI Autocomplete
数据存储为:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" },
{ iso: "fr", label: "France", country: "France" }
传递给自动完成的值是:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" }
控制台报错
Material-UI: the value provided to Autocomplete is invalid.
None of the options match with {"label":"United Kingdom","iso":"gb","country":"United Kingdom"}
.
value={}
报告类型错误
Type 'string | ICountry' is not assignable to type 'ICountry | ICountry[] | null | undefined'.
Type 'string' is not assignable to type 'ICountry | ICountry[] | null | undefined'.
问题:将数据传递给组件并没有将其设置为相应的选项,我完全不知道如何解决这个问题。
问题的代码和框:
https://codesandbox.io/s/charming-firefly-zl3qd?file=/src/App.tsx
import * as React from "react";
import { Box, Typography, TextField, Button } from "@material-ui/core";
import { Autocomplete } from "@material-ui/lab";
import "./styles.css";
import { countries } from "./countries";
import { investors } from "./investor";
import { Formik } from "formik";
interface ICountry {
iso: string;
country: string;
label: string;
}
const isoToFlag = (isoCode: string) => {
if (isoCode) {
return typeof String.fromCodePoint !== "undefined"
? isoCode
.toUpperCase()
.replace(/./g, char =>
String.fromCodePoint(char.charCodeAt(0) + 127397)
)
: isoCode;
}
return "";
};
const App: React.FC = () => {
const investor = investors.find(element => element.id === "123");
return (
<div className="App">
<Formik
initialValues={{
address: {
country: investor?.legal.address.country ? investor.legal.address.country : '',
}
}}
onSubmit={(values, actions) => {
console.log({ values, actions });
actions.setSubmitting(false);
}}
>
{({ submitForm, isSubmitting, values, setFieldValue, setValues }) => {
return (
<form>
<Autocomplete
id="country"
options={countries}
getOptionLabel={option => option.label}
value={values.address.country}
renderOption={(option: ICountry) => (
<Box display="flex" flexDirection="row" alignItems="center">
<Box mr={1}>{isoToFlag(option.iso)}</Box>
<Box>
<Typography variant="body2">{option.label}</Typography>
</Box>
</Box>
)}
onChange={(e: object, value: any | null) => {
console.log('do the types match?', typeof value === typeof values.address.country);
console.log('do the objects match?', value === values.address.country);
console.log('the objects in question', value, values.address.country);
console.log(" ");
setFieldValue("address.country", value);
}}
renderInput={params => (
<TextField
{...params}
name="address.country"
label="Country"
variant="outlined"
fullWidth
/>
)}
/>
<Button
variant="contained"
size="large"
color="primary"
disabled={isSubmitting}
onClick={submitForm}
>
Submit
</Button>
</form>
);
}}
</Formik>
</div>
);
};
export default App;
countries.ts
import { ICountry } from "./investor";
export const countries: ICountry[] = [
{
iso: "gb",
label: "United Kingdom",
country: "United Kingdom"
},
{
iso: "fr",
label: "France",
country: "France"
}
];
来自 Material-UI
的完整消息是:
Material-UI: the value provided to Autocomplete is invalid.
None of the options match with {"label":"United Kingdom","iso":"gb","country":"United Kingdom"}
.
You can use the getOptionSelected
prop to customize the equality test.
默认的相等性测试只是 ===
,因此如果您的选项是对象,则您的值必须是这些对象之一才能匹配。具有相同值的不同对象将不匹配。
但是正如消息告诉您的那样,您可以通过 getOptionSelected
属性自定义相等性测试。例如,您可以指定:
getOptionSelected={(option, value) => option.iso === value.iso}
或者您可以对所有对象属性进行深度相等性检查。
类型错误可以用 value={values.address.country as ICountry}
修正。
这是使用 v4 的沙箱的工作版本:https://codesandbox.io/s/autocomplete-getoptionselected-b6n3s
这是一个工作版本,对 v5 进行了必要的修改:
https://codesandbox.io/s/autocomplete-getoptionselected-forked-rfz625?file=/src/App.tsx
v5版本使用isOptionEqualToValue
代替getOptionSelected
isOptionEqualToValue={(option, value) =>
option.iso === value.iso
}
v5 版本还更改了 renderOption
语法,如 migration guide 中所述。
数据存储为:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" },
{ iso: "fr", label: "France", country: "France" }
传递给自动完成的值是:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" }
控制台报错
Material-UI: the value provided to Autocomplete is invalid. None of the options match with
{"label":"United Kingdom","iso":"gb","country":"United Kingdom"}
.
value={}
Type 'string | ICountry' is not assignable to type 'ICountry | ICountry[] | null | undefined'. Type 'string' is not assignable to type 'ICountry | ICountry[] | null | undefined'.
问题:将数据传递给组件并没有将其设置为相应的选项,我完全不知道如何解决这个问题。
问题的代码和框: https://codesandbox.io/s/charming-firefly-zl3qd?file=/src/App.tsx
import * as React from "react";
import { Box, Typography, TextField, Button } from "@material-ui/core";
import { Autocomplete } from "@material-ui/lab";
import "./styles.css";
import { countries } from "./countries";
import { investors } from "./investor";
import { Formik } from "formik";
interface ICountry {
iso: string;
country: string;
label: string;
}
const isoToFlag = (isoCode: string) => {
if (isoCode) {
return typeof String.fromCodePoint !== "undefined"
? isoCode
.toUpperCase()
.replace(/./g, char =>
String.fromCodePoint(char.charCodeAt(0) + 127397)
)
: isoCode;
}
return "";
};
const App: React.FC = () => {
const investor = investors.find(element => element.id === "123");
return (
<div className="App">
<Formik
initialValues={{
address: {
country: investor?.legal.address.country ? investor.legal.address.country : '',
}
}}
onSubmit={(values, actions) => {
console.log({ values, actions });
actions.setSubmitting(false);
}}
>
{({ submitForm, isSubmitting, values, setFieldValue, setValues }) => {
return (
<form>
<Autocomplete
id="country"
options={countries}
getOptionLabel={option => option.label}
value={values.address.country}
renderOption={(option: ICountry) => (
<Box display="flex" flexDirection="row" alignItems="center">
<Box mr={1}>{isoToFlag(option.iso)}</Box>
<Box>
<Typography variant="body2">{option.label}</Typography>
</Box>
</Box>
)}
onChange={(e: object, value: any | null) => {
console.log('do the types match?', typeof value === typeof values.address.country);
console.log('do the objects match?', value === values.address.country);
console.log('the objects in question', value, values.address.country);
console.log(" ");
setFieldValue("address.country", value);
}}
renderInput={params => (
<TextField
{...params}
name="address.country"
label="Country"
variant="outlined"
fullWidth
/>
)}
/>
<Button
variant="contained"
size="large"
color="primary"
disabled={isSubmitting}
onClick={submitForm}
>
Submit
</Button>
</form>
);
}}
</Formik>
</div>
);
};
export default App;
countries.ts
import { ICountry } from "./investor";
export const countries: ICountry[] = [
{
iso: "gb",
label: "United Kingdom",
country: "United Kingdom"
},
{
iso: "fr",
label: "France",
country: "France"
}
];
来自 Material-UI
的完整消息是:
Material-UI: the value provided to Autocomplete is invalid. None of the options match with
{"label":"United Kingdom","iso":"gb","country":"United Kingdom"}
. You can use thegetOptionSelected
prop to customize the equality test.
默认的相等性测试只是 ===
,因此如果您的选项是对象,则您的值必须是这些对象之一才能匹配。具有相同值的不同对象将不匹配。
但是正如消息告诉您的那样,您可以通过 getOptionSelected
属性自定义相等性测试。例如,您可以指定:
getOptionSelected={(option, value) => option.iso === value.iso}
或者您可以对所有对象属性进行深度相等性检查。
类型错误可以用 value={values.address.country as ICountry}
修正。
这是使用 v4 的沙箱的工作版本:https://codesandbox.io/s/autocomplete-getoptionselected-b6n3s
这是一个工作版本,对 v5 进行了必要的修改: https://codesandbox.io/s/autocomplete-getoptionselected-forked-rfz625?file=/src/App.tsx
v5版本使用isOptionEqualToValue
代替getOptionSelected
isOptionEqualToValue={(option, value) =>
option.iso === value.iso
}
v5 版本还更改了 renderOption
语法,如 migration guide 中所述。