I have a parent component which passes down an array as props to its child component. The child component, which displays an input field connected to redux-form and a list with the array items, saves this array in its internal state and uses a handleKeyUp() function to filter the list and return a new list every time a user types a letter in the input field. The function also updates the component state by saving the new filtered list. Here is the code for the handleKeyUp() function:
handleKeyUp () {
console.log(this.state.options) <= Here is always the initial state array
const val = (this.props.input.value).toLowerCase()
if(this.props.input.value) {
var optionsFiltered = _.filter(this.state.options, function(item){
var itemName = item.name.toLowerCase()
var itemCode = item.code.toLowerCase()
return itemName.indexOf(val)>-1 || itemCode.indexOf(val)>-1;
});
var otherOptionsFiltered = _.filter(this.state.otherOptions, function(item){
var itemName = item.name.toLowerCase()
var itemCode = item.code.toLowerCase()
return itemName.indexOf(val)>-1 || itemCode.indexOf(val)>-1;
});
this.setState({
options: optionsFiltered,
otherOptions: otherOptionsFiltered
})
}
}
Everything works just fine, the issue seems to be that the setState function inside the handleKeyUp() function has not yet finished when the user types the second letter, resulting in a visual flickering in the list (that is, I type the first letter, list gets filtered correctly, I type the second letter and I see the entire list for 1 ms and then the filtered list), so there is a re-render of the component after the setState inside of my function. I am using Redux too and I was wondering if at this point I should use Redux to handle this, or if there is something I can change to make it work with internal state. Posting the entire code if needed:
class MyDropdown extends Component {
constructor(props) {
super(props);
this.state = {
dropdownIsVisible: false,
dropdownCssClass: 'dropdown',
options: [],
otherOptions: []
}
this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = _.debounce(this.handleBlur.bind(this), 150);
this.handleClick = this.handleClick.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({
options: nextProps.options,
otherOptions: nextProps.otherOptions
})
}
handleFocus () {
this.setState({
dropdownIsVisible: true,
dropdownCssClass: ['dropdown', pageStyles.dropdown].join(' ')
})
}
handleBlur () {
this.setState({
dropdownIsVisible: false,
dropdownCssClass: 'dropdown'
})
}
handleClick (id, label, fieldName, otherField) {
this.props.input.onChange(label)
this.props.updateField("evaluationInfo", fieldName, id)
this.props.updateField("evaluationInfo", fieldName + "_display", label)
if(otherField) {
this.props.updateField("evaluationInfo", otherField, '')
this.props.updateField("evaluationInfo", otherField + "_display", '')
}
this.handleBlur()
}
handleKeyUp () {
console.log(this.state.options) <= Here is always the initial state array
const val = (this.props.input.value).toLowerCase()
if(this.props.input.value) {
var optionsFiltered = _.filter(this.state.options, function(item){
var itemName = item.name.toLowerCase()
var itemCode = item.code.toLowerCase()
return itemName.indexOf(val)>-1 || itemCode.indexOf(val)>-1;
});
var otherOptionsFiltered = _.filter(this.state.otherOptions, function(item){
var itemName = item.name.toLowerCase()
var itemCode = item.code.toLowerCase()
return itemName.indexOf(val)>-1 || itemCode.indexOf(val)>-1;
});
this.setState({
options: optionsFiltered,
otherOptions: otherOptionsFiltered
})
}
}
render() {
const {
input,
label,
options,
optionsLabel,
optionsField,
otherOptionsLabel,
otherOptions,
otherOptionsField,
showCode,
cssClass,
meta: {
touched,
error
}
} = this.props
if(options) {
var listItems = this.state.options.map((item) =>
<li key={ item.id } onClick={() => this.handleClick(item.id, item.name, optionsField, otherOptionsField) }>{ showCode ? item.code + ' - ' + item.name : item.name }</li>
)
}
if(otherOptions) {
var listOtherItems = this.state.otherOptions.map((item) =>
<li key={ item.id } onClick={() => this.handleClick(item.id, item.name, otherOptionsField, optionsField) }>{ showCode ? item.code + ' - ' + item.name : item.name }</li>
)
}
return (
<div className={ cssClass }>
<label>{ label }</label>
<input {...input } type="text" onFocus={ this.handleFocus } onBlur={ this.handleBlur } onKeyUp={ this.handleKeyUp } autoComplete="off" autoCorrect="off" spellCheck="false" />
<div className="relative">
<ul className={ this.state.dropdownCssClass }>
{ optionsLabel ? <li className={ pageStyles.optionsLabel }>{ optionsLabel }</li> : null }
{ listItems }
{ otherOptionsLabel ? <li className={ pageStyles.optionsLabel }>{ otherOptionsLabel }</li> : null }
{ otherOptions ? listOtherItems : null }
</ul>
</div>
{ touched && error && <span className="error">{ error }</span> }
</div>
)
}
}