在React中管理焦点

时间:2016-07-28 06:01:49

标签: javascript react-jsx

我正在使用React编写自动完成功能,当用户选择列表中的项目时,我希望输入更新。我的问题是当输入失去焦点时我希望菜单消失,除非用户在菜单中选择。目前,显示菜单基于名为showDropDown的属性。在render方法中我有showDropDown它构建菜单组件。似乎在菜单项上的单击侦听器之前调用了render方法,并且在调用onClick之前将其删除。

handleOnBlur = () => {
  this.setState({ showDropDown: false });
};

handleOnFocus = () => {
  this.setState({ showDropDown: true });
};
handleRenderSubComponents = (options) => {
  ...
  return options.map((option) => {
    ...
    return (<li key={displayString} className={className}>
      <span onClick={() => {this.handleOnItemSelect(option, fieldInput);}} style={{ cursor: 'pointer' }}>
        {displayString}
      </span>
    </li>);
  });
};
render() {
 ...
  return (
    <div className={className}>
      <div>
        <input
          style={{ position: 'relative' }}
          disabled={disabled}
          ref={(inputElem) => {this.inputElem = inputElem;}}
          valueLink={{ value: this.state.value, requestChange: (value) => this.handleOnChange(value) }}
          onBlur={this.handleOnBlur}
          onFocus={this.handleOnFocus}
          onKeyUp={this.handleOnKeyUp}
        />
      </div>
      <ul className="dropdown-menu dropdown-menu-justify" style={dropDownStyle} >
        {showDropDown && this.handleRenderSubComponents(options)}
      </ul>
    </div>
  );
}

如果输入失去焦点但焦点不在菜单中,我需要做的只是隐藏菜单

1 个答案:

答案 0 :(得分:0)

我认为这可以满足您的需求。关键是容器上的onMouseDown事件在输入的onBlur之前被触发。所以我们设置了一个可以在onBlur中检查的类变量,如果单击了容器,我们强制焦点回到输入。 setTimeout再次清除此变量,因此执行顺序为onMouseDown,onBlur,setTimeout回调。

jsfiddle

 class App extends React.Component {
    constructor(props) {
    super(props);
    this.onInputChange = this.onInputChange.bind(this);
    this.onInputBlur = this.onInputBlur.bind(this);
    this.onContainerMouseDown = this.onContainerMouseDown.bind(this);
    this.onOptionClick = this.onOptionClick.bind(this);

    this.state = {
      input: '',
      showList: false
    };

    this.options = ['Option 1', 'Option 2', 'Option 3', 'Option 4'];
  }
  componentWillUnmount() {
    clearTimeout(this.containerMouseDownTimeout);
  }
  onInputChange(e) {
    this.setState({input: e.target.value, showList: e.target.value.length > 2});
  }
  onInputBlur() {
    const showList = this.clickedInContainer && this.state.input.length > 2;

    this.setState({
      showList
    });

    if (showList) {
      this.input.getDOMNode().focus();
    }
  }
  onContainerMouseDown() {
    this.clickedInContainer = true;

    this.containerMouseDownTimeout = setTimeout(() => {
        this.clickedInContainer = false;
    });
  }
  onOptionClick(option) {
    this.setState({
        input: option,
      showList: false
    })
  }
  renderList() {
    return (
      <ol style={{cursor: 'pointer'}}>
                {this.options.map((o, i) => <li key={i} onClick={() => this.onOptionClick(o)}>{o}</li>)}
      </ol>);
  }
  render() {
    return (
        <div onMouseDown={this.onContainerMouseDown}>
          <input ref={ref => this.input = ref} value={this.state.input} 
            onChange={this.onInputChange} onBlur={this.onInputBlur}/>
          {this.state.showList && this.renderList()}
      </div>
    )
  }
}

React.render(<App />, document.getElementById('container'));