在 Formik 中使用 Material-UI 的自动完成组件

Using Material-UI's Autocomplete component with Formik

目前正在尝试将 Material UI 的 Autocomplete 组件与 Formik 一起使用。到目前为止,文本字段和 Material-UI 中的传统选择与 Formik 配合得非常好。实施自动完成并非如此。 Formik 的 onChange 处理程序似乎没有更新我的 city_id 的值。我知道自动完成仍然不属于 Material-UI 的核心库,但目前仍在查看是否有可能实现这样的功能。

import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form } from 'formik';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import Button from '@material-ui/core/Button';

import { cities } from '../data/cities';

import "./styles.css";

const initialValues = {
  city_id: '',
};

const submit = params => {
  alert(`Value for city_id is: ${params.city_id}`);
};

function App() {
  return (
     <Formik
      initialValues={ initialValues }
      onSubmit={ submit }
    >
      {({
        handleChange,
        values,
      }) => (
        <Form>
          <Autocomplete
            id="city_id"
            name="city_id"
            options={ cities }
            groupBy={ option => option.state }
            getOptionLabel={ option => option.name }
            style={{ width: 300 }}
            renderInput={params => (
              <TextField
                { ...params }
                onChange={ handleChange }
                margin="normal"
                label="Cities"
                fullWidth
                value={ values.city_id }
              />
            )}
          />

          <Button
            variant="contained"
            color="primary"
            type="submit"
          >
            Submit
          </Button>
        </Form>
      )}
    </Formik>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

你的问题是 handleChange 不会按照你的方式工作。

如果你看一下 handleChange docs:

General input change event handler. This will update the values[key] where key is the event-emitting input's name attribute. If the name attribute is not present, handleChange will look for an input's id attribute. Note: "input" here means all HTML inputs.

应该可以正常工作,但问题是 Autocomplete 里面的 TextField 只会在您输入内容时触发 handleChange,并且值将是文本,不是您想要的 id 或其他 属性,因此您需要将 handleChange 移动到 Autocomplete

还有一个问题,你不能在Autocomplete中使用handleChange,因为它没有引用你想要的输入,而且它的参数也不同于普通的[=20] =] 的 input,如您在 docs.

中所见

onChange
func
Callback fired when the value changes.
Signature:
function(event: object, value: any) => void
event: The event source of the callback
value: null

所以你需要做的是使用 setFieldValue 并将其传递给 Autocomplete 就像

onChange={(e, value) => setFieldValue("city_id", value)}

你需要传递你的字段名称和你想要得到的值。

这是一个working example

@vencovsky 提供的正确答案对我仍然有效 Material UI 14.10.1.

我要添加更多内容,因为我在使用 Yup 验证时将字段设置为 required

为了让它正常工作,我有以下内容: Yup 配置:

validationSchema = {
    Yup.object().shape({
        contact: Yup.string().max(255).required('Contact is required'),
    })
}

反应:

<Autocomplete
    id="contact-autocomplete"
    options={contacts}
    getOptionLabel={(contact) => `${contact?.firstName} ${contact?.lastName}`}
    onChange={(e, value) => setFieldValue("contact", value?.id || "")}
    onOpen={handleBlur}
    includeInputInList
    renderInput={(params) => (
        <TextField
            {...params}
            error={Boolean(touched.contact && errors.contact)}
            fullWidth
            helperText={touched.contact && errors.contact}
            label="Contact Person"
            name="contact"
            variant="outlined"
        />
    )}
/>

当用户单击 Autocomplete 元素时,它会触发运行 Formik onBluronOpen 并将字段标记为已触摸。如果没有选择一个项目,Formik flags 字段并显示 Contact is required 验证消息。

您必须在 Autocomplete 标签中添加 onChange = {(event, value) => handleChange(value)} 作为

import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form } from 'formik';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import Button from '@material-ui/core/Button';

import { cities } from '../data/cities';

import "./styles.css";

const [cityId,setCityId]=React.useState({city_id:''});

const handleChange=(value)=>{
  // Here is the value is a selected option label or the new typed value
  setCityId({city_id:value});
}


function App() {
  return (
     <Formik
      initialValues={ cityId }
      onSubmit={() => {
        alert(`Value for city_id is: ${cityId.city_id}`);
      }}
    >
      {({
        handleChange,
        values,
      }) => (
        <Form>
          <Autocomplete
            id="city_id"
            name="city_id"
            options={ cities }
            groupBy={ option => option.state }
            getOptionLabel={ option => option.name }
            style={{ width: 300 }}
            onChange = {(event, value) => handleChange(value)}
            renderInput={params => (
              <TextField
                { ...params }
                onChange={ handleChange }
                margin="normal"
                label="Cities"
                fullWidth
                value={ values.city_id }
              />
            )}
          />

          <Button
            variant="contained"
            color="primary"
            type="submit"
          >
            Submit
          </Button>
        </Form>
      )}
    </Formik>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

如果 onChange 不起作用,您也可以使用 onInputChange。