6

我试图弄清楚如何遵循 Formik、Material UI、React 工具的 Autocomplete 字段的文档中的说明

文档中给出的示例是:

import { Autocomplete } from 'formik-material-ui-lab';

const options = [{ title: 'The Shawshank Redemption', year: 1994 }, ...]

<Field
  name="name"
  component={Autocomplete}
  options={options}
  getOptionLabel={(option: Movie) => option.title}
  style={{ width: 300 }}
  renderInput={(params: AutocompleteRenderInputParams) => (
    <TextField
      {...params}
      error={touched['name'] && !!errors['name']}
      helperText={errors['name']}
      label="Autocomplete"
      variant="outlined"
    />
  )}
/>;

关于在 getOptionLabel 中使用 Movie 的含义,没有给出任何线索。当我尝试使用它时,Movie 带有下划线,就像 renderInput 对象中的 AutocompleteRenderInputParams 一样。我不知道为什么。

我看过这篇文章,它尝试了另一种方法,但我也无法让它发挥作用。

我有一个表格,有两个自动完成字段。目前,它看起来像这样。

当我尝试使用表单时,提交按钮挂起并且控制台日志显示:

Material-UI:getOptionLabel自动完成的方法返回 undefined 而不是 "" 的字符串。

import React, { useState } from 'react';
import { Link  } from 'react-router-dom';
import firebase, {firestore} from '../../../firebase';
import { withStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';

import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import {
  Formik, Form, Field, ErrorMessage,
} from 'formik';
import * as Yup from 'yup';
import { Autocomplete, ToggleButtonGroup } from 'formik-material-ui-lab';
import { Switch } from 'formik-material-ui';


const styles = {

};

const allCategories = [
    {value: 'culture', label: 'Culture'},
    {value: 'other', label: 'Other'},
    
];

const sharingOptions = [
    {value: 'open', label: 'Openly'},
    
    {value: 'me', label: 'Only me'},
    
];

function Contact(props) {
  const { classes } = props;
  const [open, setOpen] = useState(false);
  const [isSubmitionCompleted, setSubmitionCompleted] = useState(false);
  
  function handleClose() {
    setOpen(false);
  }

  function handleClickOpen() {
    setSubmitionCompleted(false);
    setOpen(true);
  }

  return (
    <React.Fragment>
        <Button
            // component="button"
            color="primary"
            onClick={handleClickOpen}
            style={{ float: "right"}}
            variant="outlined"
        >
            Create an Impact Metric
        </Button>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        {!isSubmitionCompleted &&
          <React.Fragment>
            <DialogTitle id="form-dialog-title">Create an Impact Metric</DialogTitle>
            <DialogContent>
              <DialogContentText>
                test form.
              </DialogContentText>
              <Formik
                initialValues={{ title: "", category: "",  sharing: "" }}
                
                onSubmit={(values, { setSubmitting }) => {
                   setSubmitting(true);
                   firestore.collection("testing").doc().set({
                    values,
                    createdAt: firebase.firestore.FieldValue.serverTimestamp()
                })
                
                  .then(() => {
                    setSubmitionCompleted(true);
                  });
                }}

                validationSchema={Yup.object().shape({
                  title: Yup.string()
                    .required('Required'),
                  category: Yup.string()
                    .required('Required'),
                  sharing: Yup.string()
                    .required('Required')  
                })}
              >
                {(props) => {
                  const {
                    values,
                    touched,
                    errors,
                    dirty,
                    isSubmitting,
                    handleChange,
                    handleBlur,
                    handleSubmit,
                    handleReset,
                  } = props;
                  return (
                    <form onSubmit={handleSubmit}>
                      <TextField
                        label="Title"
                        name="title"
                        className={classes.textField}
                        value={values.title}
                        onChange={handleChange}
                        onBlur={handleBlur}
                        helperText={(errors.title && touched.title) && errors.title}
                        margin="normal"
                        style={{ width: "100%"}}
                      />

                      
                      <Box margin={1}>
                        <Field
                            name="category"
                            component={Autocomplete}
                            options={allCategories}
                            getOptionLabel={option => option.label}
                            style={{ width: 300 }}
                            renderInput={(params: AutocompleteRenderInputParams) => (
                            <TextField
                                {...params}
                                error={touched['category'] && !!errors['category']}
                                helperText={
                                touched['category'] && errors['category']
                                }
                                label="Select Category"
                                variant="outlined"
                            />
                            )}
                        />
                      </Box>

                      <Box margin={1}>
                        <Field
                            name="sharing"
                            component={Autocomplete}
                            options={sharingOptions}
                            getOptionLabel={option => option.label}

                            style={{ width: 300 }}
                            renderInput={(params: AutocompleteRenderInputParams) => (
                            <TextField
                                {...params}
                                error={touched['sharing'] && !!errors['sharing']}
                                helperText={
                                touched['sharing'] && errors['sharing']
                                }
                                label="Select Sharing Option"
                                variant="outlined"
                            />
                            )}
                        />
                      </Box>
                      <DialogActions>
                        <Button
                          type="button"
                          className="outline"
                          onClick={handleReset}
                          disabled={!dirty || isSubmitting}
                        >
                          Reset
                        </Button>
                        <Button type="submit" disabled={isSubmitting}>
                          Submit
                        </Button>
                        {/* <DisplayFormikState {...props} /> */}
                      </DialogActions>
                    </form>
                  );
                }}
              </Formik>
            </DialogContent>
          </React.Fragment>
        }
        {isSubmitionCompleted &&
          <React.Fragment>
            <DialogTitle id="form-dialog-title">Thanks!</DialogTitle>
            <DialogContent>
              <DialogContentText>
                test
              </DialogContentText>
              <DialogActions>
                <Button
                  type="button"
                  className="outline"
                  onClick={handleClose}
                >
                  Close
                  </Button>
                {/* <DisplayFormikState {...props} /> */}
              </DialogActions>
            </DialogContent>
          </React.Fragment>}
      </Dialog>
    </React.Fragment>
  );
}

export default withStyles(styles)(Contact);

任何人都可以看到如何根据上面链接中发布的文档来使用 formik、material ui 的自动完成功能吗?

我还尝试使用常规选择表单输入。这是表单域:

<Box margin={1}>
                        <Field
                          component={TextField}
                          type="text"
                          name="category"
                          label="Category"
                          select
                          variant="outlined"
                          helperText="Select a category"
                          margin="normal"
                          style={{ width: "100%"}}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        >
                          {allCategories.map(option => (
                            <MenuItem key={option.value} value={option.value}>
                              {option.label}
                            </MenuItem>
                          ))}
                        </Field>

当我尝试这个时,我在控制台中收到一条警告,上面写着:

instrument.ts:129 Material-UI: You have provided an out-of-range value `undefined` for the select component.
Consider providing a value that matches one of the available options or ''

此警告没有任何意义 - 表单以正确填充的菜单呈现。

我还收到一条错误消息:

index.js:1 警告:一个组件正在将 undefined 类型的不受控输入更改为受控。输入元素不应从不受控切换到受控(反之亦然)。决定在组件的生命周期内使用受控输入元素还是不受控输入元素。更多信息

关于那个错误,我看过这篇文章,它建议使用值(而不是输入 - 我这样做)并将所有初始值定义为一种类型。对我来说,它们都是字符串,尽管我尝试用空数组替换选择字段。在这两种选择中,控制台中都会返回相同的错误消息。

在这一点上 - 我不在乎我使用哪个自动完成或选择,我只想让其中一个工作。

有趣的是,在这两种情况下(使用选择和自动完成),控制台都会记录以下警告:

Material-UI: You have provided an out-of-range value `undefined` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are `one`, `two`.
(anonymous) @ 0.chunk.js:141301
0.chunk.js:141301 Material-UI: You have provided an out-of-range value `undefined` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are `a`, `b`, `c`, `d`.

但是,只有一个错误实例说:

 A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: react-website -controlled-components
    in input (created by ForwardRef(SelectInput))
    in ForwardRef(SelectInput) (created by ForwardRef(InputBase))
    in div (created by ForwardRef(InputBase))
    in ForwardRef(InputBase) (created by WithStyles(ForwardRef(InputBase)))
    in Wi

此错误指向类别选择表单输入。

我还尝试将此代码沙箱中的性别选择表单字段添加到我的表单中,以查看是否可以使其正常工作。当我注释掉上述类别和共享字段,并添加一个默认值为空字符串的性别字段时,表单会加载。

该字段是:

<Field
                      name="gender"
                      label="Gender"
                      options={[
                        { value: "Male", label: "Male" },
                        { value: "Female", label: "Female" },
                        { value: "Other", label: "Other" }
                      ]}
                      component={Select}
                    />

出现性别选择字段,但宽度约为 1 厘米,选项菜单中未填充选项,我无法选择任何内容。但是表单确实会在性别字段中使用空字符串加载到 Firebase。这是进步,但还不足以向前迈进。

相同的代码沙箱显示了一个使用自动完成的字段。我尝试对其进行调整并以如下形式使用它:

<Field
                      name="gender"
                      label="Gender"
                      options={sharingOptions}
                      component={Autocomplete}
                      textFieldProps={{
                        label: sharingOptions.label
                      }}
                    />

当我尝试这样做时,我收到一条错误消息:

TypeError:renderInput 不是函数

此错误消息对我来说毫无意义,因为我没有在表单中的任何地方使用 renderInput。

当我尝试:

<Box margin={1}>
                        <Field
                          component={Select}
                          type="text"
                          name="category"
                          label="Impact Category"
                          select
                          variant="outlined"
                          helperText="Select a category"
                          margin="normal"
                          style={{ width: "100%"}}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        >
                          {allCategories.map(option => (
                            <MenuItem key={option.value} value={option.value}>
                              {option.label}
                            </MenuItem>
                          ))}
                        </Field>
                      </Box>

我没有收到任何错误,并且可以使用选项详细信息保存表单。但是,这实际上并不能解决自动完成为什么不起作用的问题。这也没有使用链接文档中显示的 Select 字段。所以我不清楚为什么这有效或为什么文档中显示的方法不起作用。

下一次尝试

使用此代码框中的自动完成示例作为指南,我尝试了:

<Field
              name="autocomplete"
              multiple
              component={Autocomplete}
              options={sharingOptions}
              getOptionLabel={(option: any) => option.title}
              style={{width: 300}}
              renderInput={(params: AutocompleteRenderInputParams) => (
                <MuiTextField
                  {...params}
                  error={touched['autocomplete'] && !!errors['autocomplete']}
                  helperText={touched['autocomplete'] && errors['autocomplete']}
                  label="Autocomplete"
                  variant="outlined"
                />
              )}
            />

与前面的示例一样,我的代码编辑器在它出现在 getOptionLabel 中的值“any”下划线,它还强调 AutocompleteRenderInputParams。我找不到任何文档来解释表单字段的这些元素的含义或作用。无论如何,我已经导入了 AutocompleteRenderInputParams,如代码沙箱中所示。

我将表单中自动完成字段的初始值设为空数组 - 尽管我注意到代码沙箱在此示例中没有设置初始值。当我尝试删除自动完成的初始值时,我得到的错误与初始值为空数组时生成的错误相同,但我也在控制台中收到一条警告,上面写着:

警告:自动完成的值不是数组,这可能会导致意外行为

当我尝试此代码时,我的控制台会记录以下错误:

TypeError:无法读取未定义的属性“toLowerCase”

Material-UI:getOptionLabel自动完成的方法返回 undefined 而不是 {"value":"open","label":"Open"} 的字符串。

4

2 回答 2

4

工作示例:

演示

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

import { Formik, Field } from "formik";
import { Autocomplete } from "formik-material-ui-lab";
import { TextField } from "@material-ui/core";

const options = [
  { title: "The Shawshank Redemption", year: 1994 },
  { title: "Inglourious Basterds", year: 2009 },
  { title: "Snatch", year: 2000 },
  { title: "3 Idiots", year: 2009 },
  { title: "Monty Python and the Holy Grail", year: 1975 }
];

function App() {
  return (
    <Formik
      initialValues={{
        autocomplete: null
      }}
    >
      {() => (
        <Field
          name="autocomplete"
          component={Autocomplete}
          options={options}
          getOptionLabel={(option) => option.title}
          style={{ width: 300 }}
          renderInput={(params) => (
            <TextField {...params} label="Autocomplete" variant="outlined" />
          )}
        />
      )}
    </Formik>
  );
}

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

于 2020-08-15T04:11:59.500 回答
1

对于其他被卡住的人来说,这是可行的——尽管我不明白为什么。上面帖子中提到的带下划线的错误仍然存​​在,我不知道如何解决它们。

分享这是一种前进的方式 - 而不是一个好的解决方案。

import React, { useState } from 'react';
import {render} from 'react-dom';

import { Link  } from 'react-router-dom';
import firebase, {firestore} from '../../../../firebase';
import { withStyles } from '@material-ui/core/styles';

import {
  Button,
  LinearProgress,
  MenuItem,
  FormControl,
  InputLabel,
  FormControlLabel,
  TextField,
  Typography,
  Box,
  Grid,
  Checkbox,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
} from '@material-ui/core';
import MuiTextField from '@material-ui/core/TextField';

import ToggleButton from '@material-ui/lab/ToggleButton';
import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft';
import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter';
import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight';
import FormatAlignJustifyIcon from '@material-ui/icons/FormatAlignJustify';

import {
  Formik, Form, Field, ErrorMessage,
} from 'formik';
import * as Yup from 'yup';
// import { Autocomplete, ToggleButtonGroup } from 'formik-material-ui-lab';
import {
  Autocomplete,
  ToggleButtonGroup,
  AutocompleteRenderInputParams,
} from 'formik-material-ui-lab';
import {
  fieldToTextField,
  TextFieldProps,
  Select,
  Switch,
} from 'formik-material-ui';




const allCategories = [
    
    {value: 'one', label: 'Col'},
    {value: 'two', label: 'Com'},
    
];


function UpperCasingTextField(props: TextFieldProps) {
  const {
    form: {setFieldValue},
    field: {name},
  } = props;
  const onChange = React.useCallback(
    event => {
      const {value} = event.target;
      setFieldValue(name, value ? value.toUpperCase() : '');
    },
    [setFieldValue, name]
  );
  return <MuiTextField {...fieldToTextField(props)} onChange={onChange} />;
}



function Glossary(props) {
  const { classes } = props;
  const [open, setOpen] = useState(false);
  const [isSubmitionCompleted, setSubmitionCompleted] = useState(false);
  
  function handleClose() {
    setOpen(false);
  }

  function handleClickOpen() {
    setSubmitionCompleted(false);
    setOpen(true);
  }

  return (
    <React.Fragment>
        <Button
            // component="button"
            color="primary"
            onClick={handleClickOpen}
            style={{ float: "right"}}
            variant="outlined"
        >
            Create a Defined Term
        </Button>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        {!isSubmitionCompleted &&
          <React.Fragment>
            <DialogTitle id="form-dialog-title">Create </DialogTitle>
            <DialogContent>
              <DialogContentText>
                
              </DialogContentText>
              <Formik
                initialValues={{ term: "",  definition: "",  category: [],  attribution: true, attributionRegion: '', context: "", relatedTerms: "", linkedTemplates: "", referenceMaterials: "" }}
                
                onSubmit={(values, { setSubmitting }) => {
                   setSubmitting(true);
                   firestore.collection("glossary").doc().set({
                    ...values,
                    createdAt: firebase.firestore.FieldValue.serverTimestamp()
                })
                
                  .then(() => {
                    setSubmitionCompleted(true);
                  });
                }}

                validationSchema={Yup.object().shape({
                  term: Yup.string()
                    .required('Required'),
                  definition: Yup.string()
                    .required('Required'),
                  category: Yup.string()
                    .required('Required'),
                  attribution: Yup.boolean()
                    .required('Required'),
                  context: Yup.string()
                    .required("Required"),
                    

                })}
              >
                {(props) => {
                  const {
                    values,
                    touched,
                    errors,
                    dirty,
                    isSubmitting,
                    handleChange,
                    handleBlur,
                    handleSubmit,
                    handleReset,
                  } = props;
                  return (
                    <form onSubmit={handleSubmit}>
                      <TextField
                        label="Term"
                        name="term"
                        className={classes.textField}
                        value={values.term}
                        onChange={handleChange}
                        onBlur={handleBlur}
                        helperText={(errors.term && touched.term) && errors.term}
                        margin="normal"
                        style={{ width: "100%"}}
                      />

                      <TextField
                        label="Meaning"
                        name="definition"
                        multiline
                        rows={4}
                        className={classes.textField}
                        value={values.definition}
                        onChange={handleChange}
                        onBlur={handleBlur}
                        helperText={(errors.definition && touched.definition) && errors.definition}
                        margin="normal"
                        style={{ width: "100%"}}
                      />

                      
                      
                      <TextField
                        label="How is it used?"
                        name="context"
                        className={classes.textField}
                        multiline
                        rows={4}
                        value={values.context}
                        onChange={handleChange}
                        onBlur={handleBlur}
                        helperText={(errors.context && touched.context) && errors.context}
                        margin="normal"
                        style={{ width: "100%"}}
                      />
                      

                      <Box margin={1}>
                        <Typography component="div" style={{ marginTop: "5vh", marginBottom: "5vh"}}>
                        Choose)?
                                <Grid component="label" container alignItems="center" spacing={1}>
                                <Grid item>Attribution</Grid>
                                <Grid item>
                                    <Field 
                                        component={Switch} 
                                        name="attribution"
                                        type="checkbox"
                                        
                                    >
                                    
                                    </Field>  
                                  </Grid>
                                <Grid item>Anonymous</Grid>
                                </Grid>
                            </Typography>
                        
                      </Box>
                      <Box margin={1}>
                        <Field
                          name="category"
                          multiple
                          component={Autocomplete}
                          options={allCategories}
                          getOptionLabel={(option: any) => option.label}
                          style={{width: 300}}
                          renderInput={(params: AutocompleteRenderInputParams) => (
                            <MuiTextField
                              {...params}
                              error={touched['autocomplete'] && !!errors['autocomplete']}
                              helperText={touched['autocomplete'] && errors['autocomplete']}
                              label="Category"
                              variant="outlined"
                            />
                          )}
                        />
                      </Box>                      
                      
                      <DialogActions>
                        <Button
                          type="button"
                          className="outline"
                          onClick={handleReset}
                          disabled={!dirty || isSubmitting}
                        >
                          Reset
                        </Button>
                        <Button type="submit" disabled={isSubmitting}>
                          Submit
                        </Button>
                        {/* <DisplayFormikState {...props} /> */}
                      </DialogActions>
                    </form>
                  );
                }}
              </Formik>
            </DialogContent>
          </React.Fragment>
        }
        {isSubmitionCompleted &&
          <React.Fragment>
            <DialogTitle id="form-dialog-title">Thanks!</DialogTitle>
            <DialogContent>
              <DialogContentText>
Thank you              </DialogContentText>
              <DialogActions>
                <Button
                  type="button"
                  className="outline"
                  onClick={handleClose}
                >
                  Close
                  </Button>
                {/* <DisplayFormikState {...props} /> */}
              </DialogActions>
            </DialogContent>
          </React.Fragment>}
      </Dialog>
    </React.Fragment>
  );
}

export default withStyles(styles)(Glossary);
于 2020-08-13T23:15:33.857 回答