I am trying to implement an autocomplete functionality to the input fields of the form (for adding meetings to the calendar). Simplified structure of components looks like this: CalendarForm > Autocomplete > FromInput + AutocompleteSuggestions (4 of each).
CalendarForm
Autocomplete
FromInput
and
AutocompleteSuggestions (both on the same level)
User should be able to type in 2 letters, the suggestions are then fetched from json-database and displayed underneath the input filed.
Each input value is stored in state in CalendarFrom via handleChange() method.
I am receiving value via props in FromInput (from CalendarForm in inputAutcomplete method) and passing it to the render method in as value={ value }. After user selects the suggestion by clicking on it, the input value should be replaced with the suggestion. Instead it is being changed from uncontrolled to controlled, which causes a warning.
Current behaviour: value in props in FormInput is undefined while it should be equal to whatever suggestion user has picked.
Expected behaviour: User picks suggestion from the rendered list and the suggestion is then set as value in this certain input. I am looking for the 'proper' solution and trying to understand mechanics behind this functionality.
This is my first react project so I kindly ask for forgiveness if the post isn't super clear. I did my best explaining what I need and I am happy to answer follow up questions.
Here is the simplified code:
CalendarForm.js
export default class CalendarForm extends Component {
constructor(props) {
super(props)
this.api = new API();
this.state = {
...this.initializeEmptyState(),
errors: []
}
}
inputAutcomplete(inputKey, attributes) {
return (
<Autocomplete
value={ this.state[inputKey] }
onChange={ this.handleCompletion }
>
<FormInput
{ ...attributes }
onChange={ this.handleChange }
value={ this.state[inputKey] } // should it be this.props.usersSelection?
/>
</Autocomplete>
)
}
displayFormFields = () => {
const { formFields } = this.props;
const allFields = formFields.map(field => {
const { name, placeholder, label, isAutocomplete } = field;
return (
<div key={ name }>
<label className={ placeholder }>
<h2 className="calendar__form-input-header">{ label }</h2>
{ (isAutocomplete) ?
this.inputAutcomplete(name, field)
:
this.inputRegular(name, field)
}
</label>
</div>
)
});
return allFields;
}
render() {
return (
<div className="calendar__form-component">
<form
onSubmit={ this.handleSubmit }
className="calendar__form"
>
{ this.displayFormFields() }
<button className="calendar__form-submit-btn" type="submit">Add meeting!</button>
</form>
</div>
)
}
Autocomplete.js
export default class Autocomplete extends Component {
constructor(props) {
super(props)
this.api = new Api();
this.state = {
records: [],
displayRecords: true
};
}
handleKeyUp = e => {
const { name, value } = e.target;
this.getSuggestions(name, value);
}
handleSuggestionSelect = e => {
const userPick = e.target.innerText;
this.props.onChange(e)
}
render() {
return (
<div
className="calendar__form-autocomplete"
onKeyUp={ this.handleKeyUp }
>
{ this.props.children }
<AutocompleteSuggestions
suggestions={ this.state.records }
handleSuggestion={ this.handleSuggestionSelect }
/>
</div>
)
}
FormInput.js
export default class FormInput extends Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value
}
}
render() {
const { name, className, onChange, placeholder, type, value, autocomplete } = this.props;
return (
<input
className={ className }
placeholder={ placeholder }
type={ type }
onChange={ onChange }
name={ name }
value={ value }
autoComplete={ autocomplete }
/>
)
}
}
AutocompleteSuggestions.js
export default class AutocompleteSuggestions extends Component {
render() {
const { suggestions } = this.props;
const suggestionsList = suggestions.map(suggestion => {
return (
<li
className="calendar__form-input-suggestion"
onClick={ this.props.handleSuggestion }
key={ suggestion }
>{ suggestion }</li>
)
})
return (
<ul className="calendar__form-input-suggestion-list">
{ suggestionsList }
</ul>
)
}
}