javascript - MaterialUI-komponent fyller inte i automatiskt när data finns tillgänglig


Jag kan inte få min komponent att visa mina autosuggestions. Det observeras i konsolen att mina data är tillgängliga och att jag skickade dem till denna komponent med propsen suggestions, med Material UI AutoComplete-komponentfunktionen här försöker jag sätta mina alternativ, och dessa ändras när jag skriver eftersom det hanteras i en föräldrakomponent, men att sätta värdena verkar varken återspeglas eller visa mina förslag. Jag är mycket förvirrad. Min kod är nedan.

import React, { FunctionComponent, FormEvent, ChangeEvent } from "react";
import { Grid, TextField, Typography } from "@material-ui/core";
import { CreateProjectModel, JobModel } from "~/Models/Projects";
import ErrorModel from "~/Models/ErrorModel";
import Autocomplete from "@material-ui/lab/Autocomplete";

type CreateProjectFormProps = {
    model: CreateProjectModel;
    errors: ErrorModel<CreateProjectModel>;
    onChange: (changes: Partial<CreateProjectModel>) => void;
    onSubmit?: () => Promise<void>;
    suggestions: JobModel[];
};

const CreateProjectForm: FunctionComponent<CreateProjectFormProps> = ({
    model,
    errors,
    onChange,
    onSubmit,
    suggestions,
}) => {
    const [open, setOpen] = React.useState(false);
    const [options, setOptions] = React.useState<JobModel[]>([]);
    const loading = open && options.length === 0;
    const [inputValue, setInputValue] = React.useState('');
    React.useEffect(() => {
        let active = true;

        if (!loading) {
            return undefined;
        }

        (async () => {
            if (active) {
                setOptions(suggestions);
            }
        })();

        return () => {
            active = false;
        };
    }, [loading]);

    React.useEffect(() => {
        if (!open) {
            setOptions([]);
        }
    }, [open]);

    const submit = async (event: FormEvent) => {
        event.preventDefault();
        event.stopPropagation();

        await onSubmit();
    };

    const change = (name: string) => (event: ChangeEvent<HTMLInputElement>) => {
        setInputValue(event.target.value);

        onChange({
            [name]: event.target.value,
        });
    };

    const getFieldProps = (id: string, label: string) => {
        return {
            id,
            label,
            helperText: errors[id],
            error: Boolean(errors[id]),
            value: model[id],
            onChange: change(id),
        };
    };

    return (
        <Autocomplete
            {...getFieldProps}
            open={open}
            onOpen={() => {
                setOpen(true);
            }}
            onClose={() => {
                setOpen(false);
            }}
            getOptionSelected={(option, value) => option.id === value.id}
            getOptionLabel={(option) => option.id}
            options={options}
            loading={loading}
            autoComplete
            includeInputInList            
            renderInput={(params) => (
                <TextField
                    {...getFieldProps("jobNumber", "Job number")}
                    required
                    fullWidth
                    autoFocus
                    margin="normal"
                />
            )}
            renderOption={(option) => {        
                return (
                  <Grid container alignItems="center">

                    <Grid item xs>
                      {options.map((part, index) => (
                        <span key={index}>
                          {part.id}
                        </span>
                      ))}
                      <Typography variant="body2" color="textSecondary">
                        {option.name}
                      </Typography>
                    </Grid>
                  </Grid>
                );
              }}            
        />
    );
};

export default CreateProjectForm;

Exempel på mina data i förslag ser ut så här:

[{"id":"BR00001","name":"Aircrew - Standby at home base"},{"id":"BR00695","name":"National Waste"},{"id":"BR00777B","name":"Airly Monitor Site 2018"},{"id":"BR00852A","name":"Cracow Mine"},{"id":"BR00972","name":"Toowoomba Updated"},{"id":"BR01023A","name":"TMRGT Mackay Bee Creek"},{"id":"BR01081","name":"Newman Pilot Job (WA)"},{"id":"BR01147","name":"Lake Vermont Monthly 2019"},{"id":"BR01158","name":"Callide Mine Monthly Survey 2019"},{"id":"BR01182","name":"Lake Vermont Quarterly 2019 April"}]

Svar #1

Problemet i din kod är de useEffects som du använder.

I det nedanstående useEffect sätter du faktiskt alternativen till en tom array initialt. Det beror på att din autokomplettering inte är öppen och effekten körs även vid första mount. Eftersom du dessutom sätter alternativ i en annan useEffect är det enda gången din kod förväntas fungera när loading-state uppdateras och du inte har öppnat autokompletteringsrullgardinsmenyn.

Ögonblicket du stänger det ens en gång uppdateras tillståndet tillbaka till tomt och du kommer inte längre att se några förslag.

React.useEffect(() => {
    if (!open) {
        setOptions([]);
    }
}, [open]);

Lösningen är enkel. Du behöver inte hålla ett lokalt tillstånd för alternativ utan använda värdena som kommer från props, vilket är suggestions

Du behöver bara hålla ett tillstånd för öppet

const CreateProjectForm: FunctionComponent<CreateProjectFormProps> = ({
    model,
    errors,
    onChange,
    onSubmit,
    suggestions,
}) => {
    const [open, setOpen] = React.useState(false);
    const loading = open && suggestions.length === 0;
    const [inputValue, setInputValue] = React.useState('');

    const submit = async (event: FormEvent) => {
        event.preventDefault();
        event.stopPropagation();

        await onSubmit();
    };

    const change = (name: string) => (event: ChangeEvent<HTMLInputElement>) => {
        setInputValue(event.target.value);

        onChange({
            [name]: event.target.value,
        });
    };

    const getFieldProps = (id: string, label: string) => {
        return {
            id,
            label,
            helperText: errors[id],
            error: Boolean(errors[id]),
            value: model[id],
            onChange: change(id),
        };
    };

    return (
        <Autocomplete
            {...getFieldProps}
            open={open}
            onOpen={() => {
                setOpen(true);
            }}
            onClose={() => {
                setOpen(false);
            }}
            getOptionSelected={(option, value) => option.id === value.id}
            getOptionLabel={(option) => option.id}
            options={suggestions}
            loading={loading}
            autoComplete
            includeInputInList            
            renderInput={(params) => (
                <TextField
                    {...getFieldProps("jobNumber", "Job number")}
                    required
                    fullWidth
                    autoFocus
                    margin="normal"
                />
            )}
            renderOption={(option) => {        
                return (
                  <Grid container alignItems="center">

                    <Grid item xs>
                      {options.map((part, index) => (
                        <span key={index}>
                          {part.id}
                        </span>
                      ))}
                      <Typography variant="body2" color="textSecondary">
                        {option.name}
                      </Typography>
                    </Grid>
                  </Grid>
                );
              }}            
        />
    );
};

export default CreateProjectForm;

Svar #2

Jag lade märke till några problem med din kod, getFieldProps kallas utan id- eller name-parametrar vilket gör att sidan inte laddas. Viktigare är att du bör överväga att följa autokompletteringsdokumentationen när du skickar och använder props till den. Till exempel:

renderInput={(params) => <TextField {...params} label="Controllable" variant="outlined" />}

Jag ställde några frågor, vänligen låt mig veta när du kan få fram de svaren så att jag kan ta itu med alla problem som kan uppstå.

Q1. Bör användarinmatningen ge relevanta träffar från namn-egenskapen i dina förslag eller bara id? Till exempel, om jag skriver "lake", vill du då visa BRO1182, Lake Vermont Quarterly 2019 April som en träff?

Q2. hur ville du hantera felaktiga fall? Jag ser att du har en felmodell, men är osäker på hur du vill använda den för att styla autokomplettering när ett fel inträffar

Q3. saknas en skicka-knapp? Jag ser onSubmit-funktionen men den används inte i vår kod.

Q4. Finns det någon särskild anledning till att du behöver de öppna och laddande tillstånden?

nedan är vad jag har försökt hittills för att visa relaterade matchningar från användarens inmatning

import React, { FunctionComponent, FormEvent, ChangeEvent } from "react";
import { Grid, TextField, Typography } from "@material-ui/core";
import { CreateProjectModel, JobModel } from "~/Models/Projects";
import ErrorModel from "~/Models/ErrorModel";
import Autocomplete from "@material-ui/lab/Autocomplete";

type CreateProjectFormProps = {
  model: CreateProjectModel;
  errors: ErrorModel<CreateProjectModel>;
  onChange: (changes: Partial<CreateProjectModel>) => void;
  onSubmit?: () => Promise<void>;
  suggestions: JobModel[];
};

const CreateProjectForm: FunctionComponent<CreateProjectFormProps> = ({
  model,
  errors,
  // mock function for testing
  // consider a better name like selectChangeHandler
  onChange = val => console.log(val),
  // consider a better name like submitJobFormHandler
  onSubmit,
  suggestions: options = [
    { id: "BR00001", name: "Aircrew - Standby at home base" },
    { id: "BR00695", name: "National Waste" },
    { id: "BR00777B", name: "Airly Monitor Site 2018" },
    { id: "BR00852A", name: "Cracow Mine" },
    { id: "BR00972", name: "Toowoomba Updated" },
    { id: "BR01023A", name: "TMRGT Mackay Bee Creek" },
    { id: "BR01081", name: "Newman Pilot Job (WA)" },
    { id: "BR01147", name: "Lake Vermont Monthly 2019" },
    { id: "BR01158", name: "Callide Mine Monthly Survey 2019" },
    { id: "BR01182", name: "Lake Vermont Quarterly 2019 April" }
  ]
}) => {
  const [value, setValue] = React.useState<JobModel>({});
  const loading = open && options.length === 0;

  // this pc of code is not used, why?
  const submit = async (event: FormEvent) => {
    event.preventDefault();
    event.stopPropagation();

    await onSubmit();
  };

  const handleChange = (_: any, value: JobModel | null) => {
    setValue(value);

    onChange({
      [value.name]: value.id
    });
  };

  // consider passing in props instead
  const getFieldProps = (id: string, label: string) => {
    return {
      id,
      label,
      // not sure what this is
      helperText: errors[id],
      // not sure what this is
      error: Boolean(errors[id]),
      value: model[id],
      onChange: change(id)
    };
  };

  return (
    <Autocomplete
      id="placeholder-autocomplete-input-id"
      // for selection, use value see docs for more detail
      value={value}
      onChange={handleChange}
      getOptionSelected={(option, value) => option.id === value.id}
      getOptionLabel={option => option.id}
      options={options}
      loading={loading}
      autoComplete
      includeInputInList
      renderInput={params => (
        // spreading the params here will transfer native input attributes from autocomplete
        <TextField
          {...params}
          label="placeholder"
          required
          fullWidth
          autoFocus
          margin="normal"
        />
      )}
      renderOption={option => (
        <Grid container alignItems="center">
          <Grid item xs>
            <span key={option}>{option.id}</span>
            <Typography variant="body2" color="textSecondary">
              {option.name}
            </Typography>
          </Grid>
        </Grid>
      )}
    />
  );
};

export default CreateProjectForm;

och du kan se koden köras i min codesandbox genom att klicka på knappen nedan

Relaterade artiklar