web-dev-qa-db-fra.com

Problème d'égalité de typescript sur la saisie semi-automatique de l'interface utilisateur matérielle

Les données sont stockées sous:

 { iso: "gb", label: "United Kingdom", country: "United Kingdom" },
 { iso: "fr", label: "France", country: "France" }

La valeur transmise à la saisie semi-automatique est:

{ iso: "gb", label: "United Kingdom", country: "United Kingdom" }

Erreur signalée dans la console

Material-UI: la valeur fournie à Autocomplete n'est pas valide. Aucune des options ne correspond à {"label":"United Kingdom","iso":"gb","country":"United Kingdom"}.

Erreur de type signalée le value={}

Tapez la chaîne | ICountry 'n'est pas attribuable au type' ICountry | ICountry [] | null | indéfini'. Le type "chaîne" ne peut pas être affecté au type "ICountry | ICountry [] | null | indéfini'.

Problème: la transmission de données au composant ne le définit pas sur l'option correspondante, je n'ai plus d'idées sur la façon de résoudre ce problème.

Codesandbox d'émission: 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;

pays.ts


import { ICountry } from "./investor";

export const countries: ICountry[] = [
  {
    iso: "gb",
    label: "United Kingdom",
    country: "United Kingdom"
  },
  {
    iso: "fr",
    label: "France",
    country: "France"
  }
];
1
Xavier

Le message complet de Material-UI est:

Material-UI: la valeur fournie à Autocomplete n'est pas valide. Aucune des options ne correspond à {"label":"United Kingdom","iso":"gb","country":"United Kingdom"}. Vous pouvez utiliser le prop getOptionSelected pour personnaliser le test d'égalité.

Le test d'égalité par défaut est simplement ===, donc si vos options sont des objets, votre valeur devra être l'un de ces objets exacts pour correspondre. Un objet différent avec les mêmes valeurs ne correspondra pas.

Mais comme le message vous l'indique, vous pouvez personnaliser le test d'égalité via la propriété getOptionSelected. Par exemple, vous pouvez spécifier:

getOptionSelected={(option, value) => option.iso === value.iso}

ou vous pouvez effectuer une vérification d'égalité approfondie de toutes les propriétés de l'objet.

L'erreur de type peut être corrigée avec value={values.address.country as ICountry}.

Voici une version fonctionnelle de votre sandbox: https://codesandbox.io/s/autocomplete-getoptionselected-b6n3s

7
Ryan Cogswell