I'm trying to figure out how to follow the instructions in the documentation for the Autocomplete field of the Formik, Material UI, React tool here.
The example given in the documentation is:
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"
/>
)}
/>;
No clues are given as to the meaning of Movie where it is used in getOptionLabel. When I try to use this, Movie is underlined as is AutocompleteRenderInputParams in the renderInput object. I don't know why.
I have seen this post which tries an alternative approach, but I can't get that to work either.
I have a form, with two Autocomplete fields. Currently, it looks like this.
When I try to use the form, the submit button hangs and the console log says:
Material-UI: The
getOptionLabel
method of Autocomplete returned undefined instead of a string for "".
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);
Can anyone see how to get the autocomplete working with formik, material ui in line with the documentation published at the link above?
I also tried using the regular select form input. This is the form field:
<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>
When I try this, I get a warning in the console that says:
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 ''
This warning doesn't make any sense - the form renders with a menu correctly populated.
I also get an error that says:
index.js:1 Warning: 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
In relation to that error, I have seen this post, which recommends using value (rather than input - which I do) and defining all the initial values as a type. For me, they are all strings, although I tried replacing the select fields with empty arrays. In both alternatives, the same error message is returned in the console.
At this point - I don't care which of autocomplete or select I use, I just want to get one of them working.
It is interesting that in both cases (using select and autocomplete) the console logs warnings that say:
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`.
BUT, only one instance of the error that says:
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
This error points to the category select form input.
I also tried adding the gender select form field from this code sandbox to my form to see if I could get this working. When I comment out the category and sharing fields described above, and add a gender field with a default value of an empty string, the form loads.
The field is:
<Field
name="gender"
label="Gender"
options={[
{ value: "Male", label: "Male" },
{ value: "Female", label: "Female" },
{ value: "Other", label: "Other" }
]}
component={Select}
/>
The select field for gender appears but is about 1cm wide and the options menu does not populate with options, I can't select anything. BUT the form does load to firebase with an empty string in the gender field. That's progress but not enough to move forward.
The same code sandbox shows a field that uses Autocomplete. I tried to adapt it and use it in my form as follows:
<Field
name="gender"
label="Gender"
options={sharingOptions}
component={Autocomplete}
textFieldProps={{
label: sharingOptions.label
}}
/>
When I try that, I get an error that says:
TypeError: renderInput is not a function
This error message makes no sense to me because I'm not using renderInput anywhere in the form.
When I try:
<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>
I get no errors and can save the form with the option details. However, this does not actually solve the problem about why Autocomplete will not work. This is also not using the Select field as shown in the linked documentation. So I'm no clearer on why this works or why the method shown in the documentation does not work.
NEXT ATTEMPT
Using the autocomplete example in this codesandbox as a guide, I tried:
<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"
/>
)}
/>
As with the earlier example, my code editor underlines the value "any" where it appears in getOptionLabel and it also underlines AutocompleteRenderInputParams. I can't find any documentation explaining what these elements of the form field mean or do. In any event, I have imported AutocompleteRenderInputParams as shown in the code sandbox.
I made the initial value of the autocomplete field in my form an empty array - although I note the code sandbox does not set an initial value in this example. When I try removing the initial value of autocomplete, I get the same errors as are generated when the initial value is an empty array, but I also get a warning in the console that says:
Warning: value for autocomplete is not an array, this can caused unexpected behaviour
When I try this code, my console logs the following errors:
TypeError: Cannot read property 'toLowerCase' of undefined
Material-UI: The
getOptionLabel
method of Autocomplete returned undefined instead of a string for {"value":"open","label":"Open "}.