如何使用带有 Material-UI 的 Formik 获取值可重用的自动完成组件

How to get value reusable Autocomplete component using Formik with Material-UI

我有一个可重复使用的自动完成组件,我需要获取所选对象,但我无法在组件外部访问此信息。

See that on the component I can access the information.

But on the form I can't access this information.

如何访问表单上的选定对象?

See the code in Sandbox!

谢谢你的帮助!

自动完成:

import * as React from "react";
import { FieldProps, getIn } from "formik";
import { TextField, CircularProgress } from "@material-ui/core";
import MuiAutocomplete, {
  createFilterOptions
} from "@material-ui/lab/Autocomplete";

const NewAutocomplete: React.FC<
  FieldProps & {
    label?: string,
    options: Array<{ label: string, value: number }>
  }
> = ({ textFieldProps, field, form, label, options, isLoading, ...props }) => {
  const filterOptions = createFilterOptions({
    matchFrom: "start",
    limit: 500
  });
  const errorText =
    getIn(form.touched, field.name) && getIn(form.errors, field.name);

  const valueInit = [
    {
      value: 0,
      label: ""
    }
  ];

  return (
    <MuiAutocomplete
      {...props}
      {...field}
      filterOptions={filterOptions}
      options={[...valueInit, ...options]}
      getOptionLabel={(option) => (option ? option.label : "")}
      getOptionSelected={(option, value) => option.value === value?.value}
      loading={isLoading}
      value={field.value}
      onChange={(e, value) => {
        form.setFieldValue(field.name, value);
        //console.log(value);
      }}
      renderInput={(props) => (
        <>
          <TextField
            {...props}
            {...textFieldProps}
            label={label}
            helperText={errorText?.value || errorText}
            error={!!errorText}
            InputProps={{
              ...props.InputProps,
              endAdornment: (
                <React.Fragment>
                  {isLoading ? (
                    <CircularProgress color="primary" size={20} />
                  ) : null}
                  {props.InputProps.endAdornment}
                </React.Fragment>
              )
            }}
          />
        </>
      )}
    />
  );
};

export default NewAutocomplete;

形式:

import { Grid } from "@material-ui/core";
import { Field, Form, Formik } from "formik";
import Autocomplete from "./component/Autocomplete";

const listFilms = [
  { label: "The Shawshank Redemption", value: 1 },
  { label: "The Godfather", value: 2 },
  { label: "The Godfather: Part II", value: 3 },
  { label: "The Dark Knight", value: 4 }
];

const initialValues = {
  film: {
    label: "",
    value: 0
  }
};

export default function App() {
  return (
    <div className="App">
      <Formik initialValues={initialValues}>
        {function Render({ errors, touched, isSubmitting, setFieldValue }) {
          return (
            <Form id="form">
              <Grid container direction="row">
                <Grid item xs={12}>
                  <Field
                    name="film"
                    component={Autocomplete}
                    label="Film"
                    options={listFilms}
                    onChange={(e, value) => {
                      console.log(value);
                    }}
                    textFieldProps={{
                      fullWidth: true,
                      margin: "dense",
                      variant: "outlined",
                      autoFocus: true
                    }}
                  />
                </Grid>
              </Grid>
            </Form>
          );
        }}
      </Formik>
    </div>
  );
}

我设法解决了通过 props 传递值的问题:setFieldValue

自动完成:

import * as React from "react";
import { FieldProps, getIn } from "formik";
import { TextField, CircularProgress } from "@material-ui/core";
import MuiAutocomplete, {
  createFilterOptions
} from "@material-ui/lab/Autocomplete";

const NewAutocomplete: React.FC<
  FieldProps & {
    label?: string,
    options: Array<{ label: string, value: number }>
  }
> = ({
  textFieldProps,
  field,
  form,
  label,
  options,
  isLoading,
  setFieldValue,
  ...props
}) => {
  const filterOptions = createFilterOptions({
    matchFrom: "start",
    limit: 500
  });
  const errorText =
    getIn(form.touched, field.name) && getIn(form.errors, field.name);

  const valueInit = [
    {
      value: 0,
      label: ""
    }
  ];

  return (
    <MuiAutocomplete
      {...props}
      {...field}
      filterOptions={filterOptions}
      options={[...valueInit, ...options]}
      getOptionLabel={(option) => (option ? option.label : "")}
      getOptionSelected={(option, value) => option.value === value?.value}
      loading={isLoading}
      value={field.value}
      onChange={(e, value) => {
        form.setFieldValue(field.name, value);
        if (setFieldValue) {
          setFieldValue(value);
        }
      }}
      renderInput={(props) => (
        <>
          <TextField
            {...props}
            {...textFieldProps}
            label={label}
            helperText={errorText?.value || errorText}
            error={!!errorText}
            InputProps={{
              ...props.InputProps,
              endAdornment: (
                <React.Fragment>
                  {isLoading ? (
                    <CircularProgress color="primary" size={20} />
                  ) : null}
                  {props.InputProps.endAdornment}
                </React.Fragment>
              )
            }}
          />
        </>
      )}
    />
  );
};

export default NewAutocomplete;

形式:

import { Grid } from "@material-ui/core";
import { Field, Form, Formik } from "formik";
import { useState } from "react";
import Autocomplete from "./component/Autocomplete";

const listFilms = [
  { label: "The Shawshank Redemption", value: 1 },
  { label: "The Godfather", value: 2 },
  { label: "The Godfather: Part II", value: 3 },
  { label: "The Dark Knight", value: 4 }
];

const initialValues = {
  film: {
    label: "",
    value: 0
  }
};

export default function App() {
  const [autocompleteValue, setAutocompleteValue] = useState(null);

  console.log(autocompleteValue);

  return (
    <div className="App">
      <Formik initialValues={initialValues}>
        {function Render({ formik, values, setFieldValue }) {
          return (
            <Form id="form">
              <Grid container direction="row">
                <Grid item xs={12}>
                  <Field
                    name="film"
                    component={Autocomplete}
                    label="Film"
                    options={listFilms}
                    textFieldProps={{
                      fullWidth: true,
                      margin: "dense",
                      variant: "outlined",
                      autoFocus: true
                    }}
                    setFieldValue={setAutocompleteValue}
                  />
                </Grid>
              </Grid>
            </Form>
          );
        }}
      </Formik>
    </div>
  );
}

控制台日志:

{label: "The Godfather", value: 2}