Material-ui autocomplete clear value - reactjs

I have one problem in my react code.
I use Material-ui and redux-form. I have select input like and after change this select i should reset value in . I use action 'change' from react-form and set value for textfield. But label in still remains. Can i clear or reset value in ?
<Autocomplete
options={list}
getOptionLabel={option => option.name}
onInputChange={onChange}
onChange={onChangeAutoComplete}
noOptionsText='Нет доступных вариантов'
loadingText='Загрузка...'
openText='Открыть'
renderInput={params => (
<Field
{...params}
label={label}
name={fieldName}
variant="outlined"
fullWidth
component={renderTextField}
className={classes.textField}
margin="normal"
/>
)}
/>

Using hooks on the value prop breaks the functionality of the autocomplete component ( at least for me ). Using class, and setting the local state is the same.
Luckily it is a react component, so it have a "key" prop. When the key prop changes, the component is re-rendered with the default values ( which is an empty array since nothing is selected). I used hooks in the parent component and passed the values to the key prop, whenever reset is needed.
<Autocomplete
key={somethingMeaningful} // Bool, or whatever just change it to re-render the component
//...other props
/>
Hope this helps!

Material UI Autocomplete onInputChange callback provides reason argument. If input has been changed by input, reason will be input and if you selected option then reason will be reset.
onInputChange={(event, newInputValue, reason) => {
if (reason === 'reset') {
setValue('')
return
} else {
setValue(newInputValue)
}
}}
setValue is useState and you can pass value state to autocomplete value property.

use value in your <Autocomplete /> like this:
<Autocomplete
value={this.state.value} //insert your state key here
//...other props
/>
Then clear state of that key, to clear the autocomplete field value

I am going to post a very dirty way of clearing the value of Autocomplete. Try it ONLY when nothing else works;
import React, { useRef } from 'react';
...
const autoC = useRef(null);
...
<Autocomplete
...
ref={autoC}
/>
and then when you want to clear the value;
const ele = autoC.current.getElementsByClassName('MuiAutocomplete-clearIndicator')[0];
if (ele) ele.click();

This is what worked for me.
const [name, setName] = useState('');
<Autocomplete
inputValue={name}
onChange={(e,v)=>setName(v?.name||v)}
...
/>
<Button onClick={()=>setName('')}>
Clear
</Button>

You can use something like the following to clear the autocomplete field when an item is selected.
<Autocomplete
value={null}
blurOnSelect={true} />
Note that you may also need to set clearOnBlur={true} if you're using the freeSolo option.
Source https://mui.com/api/autocomplete/#props

I achieved this by updating the inputValue prop where multiple prop is false. If you are using multiple prop, then there is a propblem (bug). Selected values does not get erased.

When I encountered this, it was when options for the autocomplete changed, and wanted to clear the input value. It wouldn't clear with just the options changing. What worked for me is adding a key value onto the autocomplete which depended on the change which necessitated clearing.

To solve this, I created a hook that watches the value state of the autocomplete and set the value of the input if the checkClear returns true;
function useAutocompleteInputClear(watch, checkClear) {
const elmRef = useRef(null);
useMemo(() => {
if (!elmRef || !elmRef.current) return;
if (!checkClear || typeof checkClear !== "function") return;
const button = elmRef.current.querySelector("button")
if (checkClear(watch) && button) {
button.click();
}
}, [watch])
return elmRef;
}
Its first argument is the value that should be watched and its second argument is a function that returns a boolean. if it is true the clearing will happen.
Also, the hook returns a ref that needs to pass as ref prop to Autocomplete.
const elmRef = useAutocompleteInputClear(value, v => !v || !v.id)
<Autocomplete ref={elmRef}
value={value}
...

using onChange property we can clear the value by clicking the clear icon in the following way
<Autocomplete
fullWidth={true}
label={'Source'}
margin={'noraml'}
multiple={false}
name={'Source'}
getOptionSelected={useCallback((option, value) => option.value === value.value)}
ref={SourceRef}
value={formValues.Source === '' ? {label: ''} : {label: formValues.Source}}
options={SourceStatus}
onChange={useCallback((e, v) => {
if (typeof v === 'object' && v !== null) {
handleInputChange(e, v) // help to set the value
} else {
handleInputChange(e, {label: ''}) // help to reset the value
}
})}
/>

In my case for multiselect freeSolo onChange props 3rd argument reason solved my all issues.
AutocompleteChangeReason can be:
blur
clear
createOption
removeOption
selectOption
and 2nd arg of this props gives u already updated list of (multiselect) value/s.
onChange={(_event, newOptions, reason) => {
setOptions(
reason === 'clear' ? [] : [...newOptions.map((o) => Number(o))],
);
}}

If you need only the selected value, set the value to an empty object and render the option to your needs.
<Autocomplete
value={{}}
onChange={handleSelectionChanged}
options={options ?? []}
getOptionLabel={x => (!x ? '' : x?.name ?? '')}
renderInput={params => <TextField {...params} label="" />}
/>

If you are using objects, you can use the following code to clear the field.
Ensure you add isOptionEqualToValue:
<Autocomplete
style={{ width: 250 }}
multiple
id="checkboxes-tags-demo"
options={list}
isOptionEqualToValue={(option, newValue) => {
return option.id === newValue.id;
}}
value={selected}
onChange={(e, val) => handleSelected(e, val)}
getOptionLabel={(option) => option.name}
renderInput={(params) => (
<TextField
{...params}
label="Add to Multiple"
placeholder="Favorites" />
)} />
Just set an empty array in your state through functions, and it'll be cleared.

Try this method:
use onChange method and pass third parameter reason and compare to clear text if reason is clear then executed this function.
<Autocomplete
onChange={(event, newValue, reason) => {
if (reason === 'clear') {
console.log("Put your clear logic here: this condition executed when clear button clicked")
setValue({ title: '', year: '' }) //for reset the value
return
}
}}
/>

One easy way to do this is to pass these props to autocomplete like this:
onChange={handleSkillChange}
inputValue=''
clearOnBlur={true}
onChange is an event handler, which stores the value in the state.
inputValue='' helps to ensure that the text field inside autocomplete will always be empty
clearOnBlur={true} helps to clear the value of the autocomplete component when it loses focus.

Related

Why does defaultValue not display anything inside a text field when using an API call (reactjs)

This is the code I have, but it's not displaying properly
<TextField
size="small"
className="typing-container"
defaultValue={thing.thingLastName}
label="Last Name"
onChange={(event) => setFirst(event.target.value)}
required
/>
when I change defaultValue to value, it displays but then you can't edit the field at all. This all displays properly when used earlier inside a h5 tag
You probably want to store the value in local state. Something like:
const MyComp => {
const [value, setValue] = useState(defaultValue)
return (
<TextField
...
onChange={(event) => setValue(event.target.value)}
value={value}
/>
)
}
then whenever your onChange event fires, it updates the value and passes it into the TextField. We don't need to use the defaultValue prop anymore because the useState hook takes a default value and sets that as the initial value for value
Try pass a state to value property, set the state to the default value that you want, later change the value using the onChange property.
function Comp () {
const {value, setValue} = useState('Default Value')
return (
<TextField
value={value}
onChange={(event) => setValue(event.target.value)}
required
/>
)
}

Clear input field reset back to defaultValue (0)

If I clear the input (by manually deleting the value) it leaves an empty field - how do I have this reset back to 0 when I manually delete the value? So as soon as the field contains no value set default to 0.
<TextField
type='number'
defaultValue='0'
InputLabelProps={{ shrink: true }}
id='standard-basic'
label='NUMBER (kg)'
value={mynumber}
onChange={(e) => setMyNumber(parseInt(e.currentTarget.value))}
name='MyNumber'
variant='outlined'
/>;
I also have a useState(0) on setMyNumber
I am assuming you are using Material UI's TextField component. As stated in the documentation, default value is only displayed when your component is uncontrolled. Here you have a controlled component.
https://mui.com/material-ui/api/text-field/
What you can do is providing onChange a function check whether e.target.value is empty ("") or not, so you can set your variable to 0 or e.target.value.
handleChange = (e) => {
if (e.target.value === "" && mynumber !== 0) {
setMyNumber(0)
} else {
setMyNumber(e.target.value)
}
}
<TextField
onChange={handleChange}
/>

Need help capturing the user choice from Autocomplete (multiple) in state (React)

Thanks for reading. I'm using Material-UI Autocomplete and trying to figure out how to capture a user choice. Here's the code. For now, I'm trying to console.log the user choice or deletion of a movie from the selection list. If I can get this to work I'll probably use a state settor in handleChange to capture the movie and read the state value for processing once the dialog is submitted. Since this is a multiple Autocomplete there may be several selected values.
Currently event.target.value in handleChange displays a zero when selecting a movie from the list, and undefined when removing. I need it to display the actual title.
Happy to consider any suggestion to get to the objective of being able to process all autocomplete selections upon dialog submission. Thanks in advance!
React version: 16.8
Resolved by sending the parameters (event, value) from onChange. Please see the onChange value in the below snippet.
export default function App() {
const classes = useStyles();
const handleChange = (value) =>
{
const x = value.map(function(a){return {title:a.title};});
console.log(x);
};
return (
<div className={classes.root}>
<Autocomplete
multiple
id="tags-outlined"
options={top100Films}
onChange={(event, value) => handleChange(value)}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[14]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
</div>
);
}

Material-ui Autocomplete warning The value provided to Autocomplete is invalid

I am working with React and material-ui.. I just realize i have a warning with the Autocomplete component when i try to submit the form, so i tried to do something really basic just like in the documentation:
let Form = props => {
return(
<form noValidate onSubmit={handleSubmit} >
<Autocomplete
id="combo-box-demo"
options={[{id:1,name:"test"},{id:2, name:"test2"}]}
getOptionLabel={(option) => option.name}
style={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
/>
and when i try to submit the form i get the following error:
Material-UI: The value provided to Autocomplete is invalid.
None of the options match with {"id":1,"name":"test"}.
You can use the getOptionSelected prop to customize the equality test.
I also realize that if i set the options in the state of the component there is no warning (just when they are set like a constant). So i wonder if some of you have any idea of this behavior? thank you so much in advance.
Basically the reason why you get the warning is a default implementation of getOptionSelected in version 4.x.x:
getOptionSelected = (option, value) => option === value
In your case, selecting a value the following comparison happens:
// option === value:
{id:1, name:"test"} === {id:1, name:"test"} // false
Obviously, it can be true in some circumstances. In this particular case, it's false because of objects pointing to the different instances.
Solution! You have to overwrite getOptionSelected implementation:
<Autocomplete
getOptionSelected={(option, value) => option.id === value.id}
...otherProps
/>
[Update]
Note, in version 5.x.x the prop was renamed:
- getOptionSelected={(option, value) => option.id === value.id}
+ isOptionEqualToValue={(option, value) => option.id === value.id}
version 5.0
isOptionEqualToValue={(option, value) => option.value === value.value}
Also when you want to build a searcher where value you write is not necesary the same as the options you can set freeSolo to true and the warning will disapear
This Worked,
getOptionSelected={(option, value) => option.value === value.value}
https://github.com/mui-org/material-ui/issues/18514#issuecomment-606854194
Following up on elVengadors Answer:
When you want to build a searcher where the value you type in the box (the inputValue) is not necessarily one of the provided options you can set freeSolo to true.
This will stop the warning message from being displayed.
The need for this might arise if you are creating a component that allows for asynchronous querying of an API. This would cause the value of options to change based on the response from the API, but options that have already been selected before changing the inputValue to query the API may not be included in this new list of options.
In the Autocomplete Component Documentation, freeSolo is described as:
If true, the Autocomplete is free solo, meaning that the user input is not bound to provided options.
Bonus
Setting freeSolo to true will remove the pop-up button (the drop-down arrow on the right side of the Autocomplete component). To retain this button, you should also add forcePopupIcon={true}.
I had same problem after adding getOptionSelected error gone.
Error:
<Autocomplete
fullWidth={true}
label={'Location'}
margin={'noraml'}
multiple={false}
name={'location'}
value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
options={location}
ref={locationRef}
onChange={useCallback((e, v) => handleInputChange(e, v))}
/>
Solution: getOptionSelected property added
<Autocomplete
fullWidth={true}
label={'Location'}
margin={'noraml'}
multiple={false}
name={'location'}
getOptionSelected={useCallback((option, value) => option.value === value.value)} // added
value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
options={location}
ref={locationRef}
onChange={useCallback((e, v) => handleInputChange(e, v))}
/>
I think you should not use <form> to wrap AutoComplete component. You should set value for AutoComplete and use a function to handle on click button to submit.
Try this:
let Form = props => {
const [value, setValue] = useState({})
const handleOnSubmit = (value) => {
setValue(value)
...
}
return(
<div>
<Autocomplete
id="combo-box-demo"
value={value}
options={[{id:1,name:"test"},{id:2, name:"test2"}]}
getOptionLabel={(option) => option.name}
style={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Combo box" variant="outlined" />}
/>
<Button onClick={() => handleOnSubmit(value)}>Submit</Button>
</div>
)
}

Focus Select element

I'm trying to programatically focus a select element in Material-UI in a useEffect block.
Trying to pass a ref to the component (ref={my ref}) itself does nothing, and trying to pass any variety of inputProps={{ ref: myRef }}, inputProps={{ inputRef: myRef }} throws an error for displayNode being undefined when I call focus() on the ref.
I am sure there is something obvious I am missing, what would be the correct syntax/way to do this?
const MyCard = ({
disabled,
}) => {
const inputField = React.useRef(null);
React.useEffect(() => {
if (!disabled && inputField.current) {
inputField.current.focus();
}
}, [inputField, disabled]);
return (
<Select
disabled={disabled}
inputProps={{ ref: inputField }}
required
id="answer"
name="answer"
autoComplete='off'
autoFocus
>
<MenuItem value="option1">option1</MenuItem>
<MenuItem value="option2">option2</MenuItem>
</Select>
)
}
Additional info from OP:
I need to be able to programmatically focus the select. Autofocus
works for me on the initial render, but not subsequently, specifically
I am trying to refocus the input after removing the disabled prop.
Make sure you check that it is defined before calling focus:
useEffect(() => {
if (myRef.current) {
myRef.current.focus()
}
}, [myRef])

Resources