MUI Autocomplete doesn't work with react-hook-form - javascript

Issue summary
I get different errors at different times. When I select a suggested option, I get the following error and warning:
Material-UI: The getOptionLabel method of Autocomplete returned undefined instead of a string for 0
Material-UI: The value provided to Autocomplete is invalid. None of the options match with 0
Plus, the option doesn't get selected and the input becomes undefined. However, when trying to choose a value a second time, it gets selected (but still shows the errors).
When I clear the input I get this error:
A component is changing the controlled value state of Autocomplete to be uncontrolled.
Elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled Autocomplete element for the lifetime of the component.
The nature of the state is determined during the first render, it's considered controlled if the value is not `undefined`.
Code for the autocomplete component
const AutocompleteUnit = ({control, label, name, ...rest}) => {
return (
<>
<Controller
onChange={([,data]) => data}
name={name}
as={
<Autocomplete
{...rest}
autoHighlight
style={{marginTop: "25px"}}
getOptionLabel={option => option.label}
renderInput={params => (
<TextField
{...params}
label={label}
variant="outlined"
/>
)}
/>
}
control={control}
defaultValue={rest.options[0]}
/>
</>
}
Options
const districtOptions = [
{ value: "ciutatvella", label: "Ciutat Vella" },
{ value: "gracia", label: "Gràcia" },
{ value: "eixample", label: "L'Eixample" },
{ value: "sarria", label: "Sarrià" }
];
Any idea on what's wrong?

just in case some stumbles upon this: you have to use defaultValue of Autocomplete instead of the Controller

Hey just use this package below:
https://www.npmjs.com/package/mui-react-hook-form-plus
import { HookAutoComplete, useHookForm } from 'mui-react-hook-form-plus ';
const Component = () => {
const {registerState, handleSubmit} = useHookForm({
defaultValues: { movie: '' },
});
const onSubmit = (data: typeof defaultValues) => {
// will run if it is valid
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<HookAutoComplete
{...registerState('movie')}
autocompleteProps={{
options: ['Fight Club', 'Top Gun', 'Titanic']
}}
textFieldProps={{
label: 'Movie',
placeholder: 'search...',
}}
/>
<button type='submit'>Submit</button>
</form>
)
}
See Demo

Related

MUI (JoyUI) Autocomplete with react-hook-form : return a different value than the label

I'm working on a form with JoyUI (material) autocomplete and react-hook-form.
I have this array of object for my options :
[
{ label: "Todo", param: "TODO" },
{ label: "In Progress", param: "IN_PROGRESS" },
{ label: "Ended", param: "ENDED" },
]
Param is the value I want to return to the API and the label the value I want to display on my autocomplete
Is it possible to display the label but return a different value to the react-hook-form (param).
Like an HTML option select (example) :
<select>
{list.map(item => (<option key={item.param} value={item.param}>{item.label}</option>))}
</select>
My Input component :
export const Input = ({label,type,param,validation,list,register}) => (
<FormControl>
<FormLabel>
{label}
</FormLabel>
<Autocomplete
options={list}
{...register(param, validation)} // react-hook-form register
/>
</FormControl>);```
Thanks,
you can use getOptionLabel
export const Input = ({label,type,param,validation,list,register}) => (
<FormControl>
<FormLabel>
{label}
</FormLabel>
<Autocomplete
options={list}
getOptionLabel={(option) => option.label} // this will show label in the dropdown
{...register(param, validation)} // react-hook-form register
/>
</FormControl>);

Setting the default value inside the input field in the antd library after calling the function in useEffect

I have the loadProfile function that I want to call in useEffect. If the loadProfile function is called within useEffect, the name Mario should be displayed inside the input name field. How can I set the default value in the antd library inside input? I try to use defaultValue but the input field remains empty.
Example here: https://stackblitz.com/edit/react-311hn1
const App = () => {
const [values, setValues] = useState({
role: '',
name: '',
email: '',
password: '',
buttonText: 'Submit'
});
useEffect(() => {
loadProfile();
}, []);
const loadProfile = () => {
setValues({...values, role, name="Mario", email});
}
const {role, name, email, buttonText} = values;
const updateForm = () => (
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
name: name
}}
>
<Form.Item
label= 'Name'
name='name'
rules={[
{
required: true,
message: 'Please input your name!',
},
]}
>
<Input
defaultValue= {name}
/>
</Form.Item>
<Form.Item
label="Email"
name="email"
value={email}
rules={[
{
type: 'email',
required: true,
message: 'Please input your email!',
},
]}
>
<Input
/>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
{buttonText}
</Button>
</Form.Item>
</Form>
);
return (
<>
<Row>
<Col span={24} style={{textAlign: 'center'}}>
<h1>Private</h1>
<p>Update Form</p>
</Col>
</Row>
{updateForm()}
</>
);
};
You have to make three changes to your code. This is a working component, I also extracted your component and put the appropriate state in there. I also made it functional.
https://stackblitz.com/edit/react-tlm1qg
First change
setValues({...values, role, name="Mario", email});
to
setValues({...values, name: "Mario"});
This will properly set the state.
Second change:
Next, you should notice that if you set defaultValue="test123" it still won't work, something is up. Remove name from Form.Item and boom it works. test123 shows up. But if you put values.name in there, it still doesn't work!
Third Change:
That's because defaultValue only sets that value right when the component is created, not on mount. So you have to use value={values.name} and it will set that value once on mount per your useEffect
In the demo component I also added a change handler for you so the user can type in there, if you wanted that.
If you look at the FAQ for Ant Design it says:
Components inside Form.Item with name property will turn into
controlled mode, which makes defaultValue not work anymore. Please try
initialValues of Form to set default value.
Ant Design is taking over control - so you don't have to set value={name} and onChange.
You want to set the values AFTER the component is created. So to do that you need
const [form] = Form.useForm();
React.useEffect(() => {
form.setFieldsValue({
username: 'Mario',
});
}, []);
and in you Form make sure you add:
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
}}
form={form} // Add this!
>
I updated my online example.
Big picture - when you want to set the value after creation, use this hook and form.setFieldsValue

MaterialUI Select set value is always out of range

i've a MaterialUI Select code, and i'm handling the value parameter dynamically. My problem is, when i set any value, it says always it's out of range, even showing the value in the valid values.
SelectInput.js:291 Material-UI: you have provided an out-of-range value `100001,250000` for the select (name="followers") component.
Consider providing a value that matches one of the available options or ''.
The available values are `0,50000`, `50001,100000`, `100001,250000`, `250001,500000`, `500001,750000`, `750001,9007199254740991`.
(anonymous) # SelectInput.js:291
And this is my code simplified:
const followers = [
{ '0-50k': [0, 50000] },
{ '50k-100k': [50001, 100000] },
{ '100k-250k': [100001, 250000] },
{ '250k-500k': [250001, 500000] },
{ '500k-750k': [500001, 750000] },
{ '+1M': [750001, Number.MAX_SAFE_INTEGER] },
];
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={(event) => setValue(event.target.value)} //I've updated the sate with the new value
value={
filters.basicInfo.followers
? value
: ''
}
variant="outlined"
>
{followers.map((element) => (
<MenuItem
value={element[Object.keys(element)]}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
As you can see in the message, the value selected 100001,250000 it's inside the range examples 100001,250000
Where is the problem?
add this defaultValue = "" like this
<Select
...
defaultValue=""
>
This advice may be useful for others:
If the value for Select element is object, it should be the exact instance of the object from the list of Options.
For example:
const [test, setTest] = useState("");
//list of options for Material UI select
const testOptions = [
{name: "123"},
{name: "456"},
{name: "769"},
];
//let's try to change value to {name: "123"} using JS
setTest(testOptions[0]); // everything is OK
setTest({name: "123"}); // Error! You provided out of range value!
Stringifying your value will get this to work.
element[Object.keys(element)] + ""}
If you needed it to be in its original array format before sending the result to your server you could use a function like this to do this
const strToArr = str => str.split(',').map(item => Number(item))
In my code here I have used your provided example and been able to replicate your error. But Stringifying the value removes the error and gets it to work as expected.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import Select from "#material-ui/core/Select";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function SimpleSelect() {
const classes = useStyles();
const followers = [
{ "0-50k": [0, 50000] },
{ "50k-100k": [50001, 100000] },
{ "100k-250k": [100001, 250000] },
{ "250k-500k": [250001, 500000] },
{ "500k-750k": [500001, 750000] },
{ "+1M": [750001, Number.MAX_SAFE_INTEGER] }
];
const [value, setValue] = React.useState("");
const handleChange = event => setValue(event.target.value);
return (
<div>
<p>value - {value}</p>
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={handleChange}
value={value}
variant="outlined"
>
{followers.map(element => (
<MenuItem
value={element[Object.keys(element)] + ""}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
</div>
);
}
I ran into the same problem (you have provided an out-of-range value) when using a number state with a default value of -1:
const [selectedAccountId, setSelectedAccountId] = useState<number>(-1);
The solution to this problem was to assign an empty string for the value property in Material's UI Select component instead of using the default value of -1:
value={selectedAccountId === -1 ? '' : selectedAccountId}
Full component example:
<FormControl fullWidth>
<InputLabel>Account</InputLabel>
<Select
id="account"
value={selectedAccountId === -1 ? '' : selectedAccountId}
onChange={event => {
setSelectedAccountId(Number(event.target.value));
}}>
{allAccounts.map((account, index) => (
<MenuItem key={index} value={account.id}>
{account.exchange} ({account.id})
</MenuItem>
))}
</Select>
</FormControl>
From some research, what I've come to understand to be the reason for this warning, in my case, is the MUI Select was trying to map over a list of options that were not available on the first render as the list was coming from Axios response.
I made a component named Dropdown that renders the MUI Select component. I was providing it four props:
options: the list of options,
initialValue: the default value as I
had different default values for different instances of the Dropdown component that were not the first item of the
options list always
... and 2 other props that are not scoped for this discussion.
So, for each instance of the Dropdown component, I had to check whether the options list has any data, and only then render it. And this is what removed the warning from the console. To give a glimpse of what I did:
{viewsData.length > 0 && (
<Dropdown
options={viewsData}
initialValue={7}
{...otherProps}
/>
)}
This was bugging me for a long time. Hopefully this will help someone.
I got the same error and I solved it by making sure that the default value and the other select values thereafter are the same, for example if the default value is a string like '' then the other values are objects it will show the warning so to avoid such a problem make the default value to be either a [] or {} or most preferably null
To add to #Ait Friha Zaid response.
I also added the defaultValue attribute but also added an additional option:
const values = ['option 1', 'option 2', 'option 3'];
<FormControl fullWidth>
<InputLabel>{title}</InputLabel>
<Select
defaultValue="choose"
label={title}
onChange={e => func({...state, [keyName]: e.target.value}) }
>
<MenuItem disabled value="choose">Choose Option</MenuItem>
{values.map((value) => (
<MenuItem value={value} key={value}>{value}</MenuItem>
))}
</Select>
</FormControl>
That way you always have a disabled option that works as a placeholder which is the default option, and in case you want to do form validation, until the user changes the option, the state wont be changed.

How to update the option values in dropdown on child component from parent component state in react js?

I have a requirement like, In parent component i'm getting data from api and pass that array of data to DataTable child component to display in tablular format. In that, I need to display a drop-down for each column and option are predefined(No need of dynamic values). I need only, when the user select the dropdown values, it should update in parent component state and populates that selected in particular drop-down component.
Here is what i tried,
Parent component for storing dropdown values ::
let [schema, setSchema] = useState([])
const handleChange = (event, index) => {
setSchema([
...schema,
{
Name: event.target.name,
Type: event.target.value
}
]);
};
DataTable component inside parent compoent to display data ::
<Container>
<DataTable
data={data}
meta_data={meta_data}
handleChange={handleChange}
schema={schema}
/>
</Container>
Here the iteration of each object from array to display dropdown once ::
{
Object.keys(filteredData[0]).map((field, index) => {
return (
<>
<TableCell align="left" key={field} className={classes.headings}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<FormControl className={classes.margin}>
<NativeSelect
id="demo-customized-select-native"
input={<BootstrapInput />}
onChange={(e) => props.handleChange(e, index)}
name={field}
value={props.schema.length > 0 ? props.schema[index]['Type'] : null}
>
<option value={0}>Type</option>
<option value={`str`}>string</option>
<option value={`int`}>interger</option>
<option value={`float`}>float</option>
</NativeSelect>
</FormControl>
</div>
</TableCell>
</>
)
}
)}
I want, In the value prop of NativeSelect to populate the value of schema,
The schema should looks like
[
{
Name: 'passegner',
Type: 'str'
},
{
Name: 'Month',
Type: 'float'
},
]
When i retrieving the Type field of that array based in Index. It is giving 'Type' of undefined, But i actually working when console from outside of return in child component.
value={props.schema.length > 0 ? props.schema[index]['Type'] : null} - > this line giving me the error and wroking fine when console.log() outside return.
console.log(props.schema.length > 0 ? props.schema[0]['Type']) -> it is working
How to resolve?
Any suggestions would be great.
Above the return statement put a console like
console.log(props.schema[index]['Type'])
Try to figure out what went wrong, From my Guess Object.keys(filteredData[0]) may have move array values compared to props.schema[index]. So It may throw an error.
The useState hook has the following form:
const [state, setState] = useState(initialState);
In the React docs it is written:
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.
Meaning that you should change this:
setSchema([
...schema,
{
Name: event.target.name,
Type: event.target.value
}
]);
into this:
setSchema((prevSchema) => {
return [
...prevSchema,
{
Name: event.target.name,
Type: event.target.value
}
])
};

Cannot read property 'type' of undefined while using react-select with formik

I'm building a form with an auto filling text box using react-select and formik.
<Formik
initialValues={{
assignedTo: task.assignedTo,
}}
onSubmit={(values) => {
const updatedTask = { ...task, ...values };
editTask(updatedTask);
}}
render={({ handleSubmit, handleChange, values }) => (
<form>
<Select type="text" name="assignedTo" options={options} onChange={handleChange} value={{ value: task.assignedTo, label: task.assignedTo }} />
</form>
})
/>
It throws an error Cannot read property 'type' of undefined
How to fix this and handle react-select in formik?
the first parameter of React-Select's onChange is an option value while the first parameter of formik's handleChange is an event
React-Select: onChange(value, action) => void
Formik: handleChange(e: React.ChangeEvent<any>) => void
That is the reason why you received this kind of error.
Here is my approach. I hope this helps you.
import React from 'react';
import { Formik, Form, Field } from 'formik';
import Select from 'react-select';
function SelectField(FieldProps) {
return (
<Select
options={FieldProps.options}
{...FieldProps.field}
onChange={option => FieldProps.form.setFieldValue(FieldProps.field.name, option)}
/>
)
}
export default function FormikReactSelect(props) {
const options = [
{ value: '1', label: 'White' },
{ value: '2', label: 'Yellow' },
];
return (
<Formik>
{formProps => (
<Form>
<Field name='SelectColor' options={options} component={SelectField}/>
</Form>
)}
</Formik>
);
}
As already stated, onChange handler on React-Select is different from the handleChange handler on formik.
I arrived at this from the official formik.org documentation...
Notice, I don't spread the field values like so {...field} into the react-select form because that's the problem we're trying to fix. The field values contain the onChange handler from formik which we intend to remove.
Instead, I only assign the field.name to the name attribute, and then manually set the value using the setValue handler provided through the useField helpers.
This worked for me.
import { useField, Formik } from 'formik';
import Select from 'react-select';
export default function SelectInput(props) {
const [field, meta, helpers] = useField(props);
const options = [
{ value: '1', label: 'White' },
{ value: '2', label: 'Yellow' },
];
return (
<Formik>
<Select
options={options}
onChange={(option) => helpers.setValue(option?.value)}
name={field.name}
placeholder={props.placeholder}
/>
{meta.touched && meta.error ? <div>{meta.error}</div> : null}
</div>
);
}
I had the same error but found a solution
try this
onChange={e => formik.setFieldValue('language', e[0].value)}
.setFieldValue is a Formik method that needs two arguments, first is the field name which's the value you want to update, and second, is the value
e will be an array with one or multiple selected objects, in my case, there was only one so I accessed the only object using e[0] and then bind the required property from the object, which in my case was e[0].value
Here's my very simple solution:
<Select
placeholder={`Select Flavor`}
options={[
{ value: `1`, label: `Chocolate` },
{ value: `2`, label: `Strawberry` },
{ value: `3`, label: `Vanilla` },
]}
name="flavor"
onChange={(selectedOption, _) =>
setFieldValue(`flavor`, selectedOption)
}
onBlur={handleBlur}
value={values.flavor}
/>
Works perfectly.

Categories