React JS - setState和不需要的重新渲染
问题描述:
我有一个父组件,作为道具传递给它的子组件。显示连接到redux-form的输入字段和包含数组项的列表的子组件将该数组保存在其内部状态中,并使用handleKeyUp()函数过滤列表并每次用户键入时返回一个新列表输入字段中的一个字母。该功能还通过保存新的过滤列表来更新组件状态。 下面是handleKeyUp()函数的代码:React JS - setState和不需要的重新渲染
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
})
}
}
一切都运行得很好,这个问题似乎是在handleKeyUp()函数内的setState功能尚未完成当用户键入的第二个字母,导致列表中的视觉闪烁(即,我键入第一个字母,列表被正确过滤,我键入第二个字母,我看到整个列表1毫秒,然后过滤列表),所以有一个重新 - 在我的函数内部的setState之后显示组件。我也在使用Redux,我想知道在这一点上我应该使用Redux来处理这个问题,或者如果有什么我可以改变的,以便使它与内部状态一起工作。 如果需要发布整个代码:
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>
)
}
}
答
您应该保存过滤列表进入状态,只有当检索完全完成。你可以使用generator function来实现这一点,并保持一个标志。
当触发KeyUp事件时,将该标志设置为'挂起'并将结果存入生成器函数中。一旦结果获取完成,将标志设置为发生器函数内的“完成”。
只有当标记“完成”时才会显示结果。
编辑:
一些示例代码
function* getResult() {
yield this.setState({status: 'pending'})
const result = yield callApi()
yield this.setState({listOfItems: result})
yield this.setState({status: 'done'})
}
不知道发电机的功能,将考虑这一点。如果你能提供一个上述实现的例子,那会很好。 –