React JS - setState和不需要的重新渲染

时间:2017-08-14 09:24:04

标签: reactjs

我有一个父组件,它将一个数组作为props传递给它的子组件。子组件显示连接到redux-form的输入字段和带有数组项的列表,将此数组保存在其内部状态,并使用handleKeyUp()函数过滤列表并在每次用户键入时返回新列表输入字段中的一个字母。该功能还通过保存新的筛选列表来更新组件状态。 这是handleKeyUp()函数的代码:

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>
    )
  }
}

1 个答案:

答案 0 :(得分:1)

只有在完全检索完成后,才应将已过滤的列表保存到状态。您可以使用generator function实现此目的并保持标记。

触发KeyUp事件时,将标记设置为&#39; pending&#39;并在生成器函数中获取结果。完成结果提取后,将标记设置为“完成”&#39;内部发电机功能。

仅在标记完成后才能渲染结果。

编辑:

一些示例代码

function* getResult() {
  yield this.setState({status: 'pending'})
  const result = yield callApi()
  yield this.setState({listOfItems: result})
  yield this.setState({status: 'done'})
}
相关问题