'Typescript Equality issue on Material UI Autocomplete
Data is stored as:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" },
{ iso: "fr", label: "France", country: "France" }
Value passed to the Autocomplete is:
{ iso: "gb", label: "United Kingdom", country: "United Kingdom" }
Error reported in console
Material-UI: the value provided to Autocomplete is invalid. None of the options match with
{"label":"United Kingdom","iso":"gb","country":"United Kingdom"}
.
Type error reported on value={}
Type 'string | ICountry' is not assignable to type 'ICountry | ICountry[] | null | undefined'. Type 'string' is not assignable to type 'ICountry | ICountry[] | null | undefined'.
Issue: Passing in data to the component doesn't set it to the corresponding option, I'm all out of ideas on how to resolve this issue.
Codesandbox of issue: 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"
}
];
Solution 1:[1]
The full message from Material-UI
is:
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.
The default equality test is simply ===
, so if your options are objects, your value would have to be one of those exact objects in order to match. A different object with the same values will not match.
But as the message tells you, you can customize the equality test via the getOptionSelected
prop. For instance you could specify:
getOptionSelected={(option, value) => option.iso === value.iso}
or you could do a deep equality check of all the object properties.
The type error can be fixed with value={values.address.country as ICountry}
.
Here is a working version of your sandbox using v4: https://codesandbox.io/s/autocomplete-getoptionselected-b6n3s
Here is a working version with the modifications necessary for v5: https://codesandbox.io/s/autocomplete-getoptionselected-forked-rfz625?file=/src/App.tsx
The v5 version uses isOptionEqualToValue
instead of getOptionSelected
isOptionEqualToValue={(option, value) =>
option.iso === value.iso
}
The v5 version also changes the renderOption
syntax as outlined in the migration guide.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |