I have set up an autocomplete with downshift and it works perfectly... except it gives me the following warning whenever i select an option.
Warning: A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
As far as i can tell (by looking at what i set the value to before and after the selection) I am not changing the input to undefined or null (what most commonly causes this issue).
My downshift version is 1.31.16
My node version is 9.2.1
My npm version is 5.10.0
My material ui version is 1.2.3
My code is as follows
import React from 'react';
import PropTypes from 'prop-types';
import Downshift from 'downshift';
import TextField from '@material-ui/core/TextField';
import Paper from '@material-ui/core/Paper';
import Avatar from '@material-ui/core/Avatar';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemText from '@material-ui/core/ListItemText';
export class Autocomplete extends React.PureComponent {
static propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
onSelect: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
value: PropTypes.string,
inputDetails: PropTypes.shape({}).isRequired,
disconect: PropTypes.bool,
};
static defaultProps = {
value: '',
disconect: false,
}
getSuggestions = (items, inputValue, disconect) => {
if(disconect){return []}
return items.filter(suggestion => {
const keep = !inputValue ||
(suggestion.primary || '').toLowerCase().match(inputValue.toLowerCase()) ||
(suggestion.secondary && suggestion.secondary.toLowerCase().match(inputValue.toLowerCase()));
return keep;
});
}
render() {
const { onSelect, onChange, value, items, inputDetails, disconect } = this.props;
return (
<Downshift
style={{position: 'relative'}}
onChange={(selection, functions) => onSelect(selection, functions)}
onStateChange={({inputValue, type}) => {
if(type === '__autocomplete_change_input__' || type === '__autocomplete_click_item__'){
onChange((inputValue || ''));
}
}}
itemToString={(item) => { return item ? item.primary : ''}}
render={({
getInputProps,
getItemProps,
getLabelProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
clearSelection,
}) => (
<div>
<TextField
InputProps={{
...getInputProps(),
placeholder: inputDetails.placeholder,
name: inputDetails.name,
id: inputDetails.id,
value,
}}
label={inputDetails.label}
fullWidth={inputDetails.fullWidth}
/>
{isOpen &&
<Paper style={{
position: 'absolute',
zIndex: 1,
}}>
{
this.getSuggestions(items, inputValue, disconect)
.map((item, index) => (
<MenuItem
{...getItemProps({
item: item,
})}
key={item._id}
style={{ height: 50 }}
>
{inputDetails.avitar &&
<Avatar>{item.primary[0]}</Avatar>
}
<ListItemText
primary={item.primary}
secondary={item.secondary}
/>
</MenuItem>
))
}
</Paper>
}
</div>
)}
/>
);
}
}
export default (Autocomplete);
Reproducing Bug
To reproduce i type into the autofill field created and select an option from the suggestions that come up.
when a suggestion is selected it creates a warning that I am changing from a controlled to uncontrolled input.
Hi, could you please reproduce your issue in codesandbox? You can start with this one: http://kcd.im/ds-example
It's just really hard to debug your issue with a wall of unformatted code 馃槄
Without spending too much time on it, I think that this should be fixed with:
<TextField
InputProps={getInputProps({
placeholder: inputDetails.placeholder,
name: inputDetails.name,
id: inputDetails.id,
value,
})}
label={inputDetails.label}
fullWidth={inputDetails.fullWidth}
/>
This way downshift can compose things together properly.
Sorry about the wall of text it was formatted when i put it in i swear. the problem persists in my code after making your change but when i created the sandbox it did not give the error. Think it might be a version issue (sandbox version is higher than mine)? I don't know ill keep digging.
Go ahead and keep digging. If you find a bug, feel free to comment here.
I would think based on your example that you should control the inputValue prop directly on the Downshift component like this:
<Downshift inputValue={this.props.value} ... />
I'm running into this same issue even though I'm controlling it like that 鈽濓笍 but maybe I'm doing it wrong. Its hard to find a full-fledged "controlled" Downshift example anywhere and the docs don't fully explain how to control everything with a concrete example. Once I figure it out I'll try to post it up so others can know how to do it.
I figured it out. There were states (mostly initially) where I was passing null or undefined values for selectedItem and inputValue. I changed it to pass "" in the cases where a value is null or undefined which has resolved the console error. Might be good to update the docs to point this out more clearly.
I got this error when I had:
<Downshift itemToString={(item) => (item ? item.value : "")}>
but my item didn't have value key. Switch to item.name(as my data had name key) helped
I was experiencing this issue as well. Solved it by setting value on the input:
<input
{...getInputProps({
type: "search",
placeholder: "Search",
id: "search",
name: "search",
className: loading ? "loading" : "",
value: props.value,
onChange: e => {
e.persist();
onChange(e, hits, refine);
}
})}
/>
Most helpful comment
I figured it out. There were states (mostly initially) where I was passing
nullorundefinedvalues forselectedItemandinputValue. I changed it to pass""in the cases where a value isnullorundefinedwhich has resolved the console error. Might be good to update the docs to point this out more clearly.