Раскрывающийся список ANTD не должен закрываться при нажатии на поле ввода

#javascript #reactjs #antd

Вопрос:

Я пытаюсь реализовать раскрывающуюся функциональность с использованием компонентов ANTD, таких как:

  1. Если в поле ввода есть какой-то текст, должно отображаться выпадающее меню.
  2. Когда я нажимаю за пределами раскрывающегося списка, раскрывающийся список должен закрыться.
  3. Когда я нажимаю на раскрывающееся меню, раскрывающееся меню не должно закрываться.
  4. Если в поле ввода нет текста, раскрывающийся список не должен открываться.
  5. Раскрывающийся список не должен закрываться при нажатии на поле ввода, если там есть какой-то текст.

Я не могу выполнить 5-е требование. Всякий раз, когда я нажимаю на поле ввода, раскрывающийся список открывается и закрывается, как показано здесь: введите описание изображения здесь

Этого не должно произойти.

Пример рабочего кода:

 state = {
    visible: false,
    searchInput: null
  };

handleVisibleChange = flag => {
    if (!this.isDefined(this.state.searchInput)) {
      this.removeDropdown();
    } else {
      this.setState({ visible: flag });
    }
  };

removeDropdown = () => {
  this.setState({ visible: false });
};

showDropdown = () => {
  this.setState({ visible: true });
};

checkInput = value => {
  if (this.isDefined(value)) {
    this.setState({ searchInput: value });
    this.showDropdown();
  } else {
    this.setState({ searchInput: null });
    this.removeDropdown();
  }
};

render() {
  const menu = (
    <Menu>
      <Menu.Item key="1">Clicking me will not close the menu.</Menu.Item>
      <Menu.Item key="2">Clicking me will not close the menu also.</Menu.Item>
      <Menu.Item key="3">Clicking me will not close the menu.</Menu.Item>
    </Menu>
  );
  return (
    <Dropdown
      overlay={menu}
      trigger={['click']}
      onVisibleChange={this.handleVisibleChange}
      visible={this.state.visible}
    >
      <Search
        value={this.state.searchInput}
        onInput={e => this.checkInput(e.target.value)}
      />
    </Dropdown>
  );
}
 

Полный код: https://stackblitz.com/edit/react-srz4ml?file=index.js

Я пробовал использовать onBlur() и onFocus() методы вместо onVisibleChange() , но это нарушает 3-е требование.

Может кто-нибудь, пожалуйста, помочь мне в этом?

Спасибо.

Ответ №1:

Я немного поэкспериментировал с этим, и, похоже, не существует супер простого способа сделать это, но я смог заставить его работать с некоторым уродливым управлением государством. https://stackblitz.com/edit/react-srz4ml-p6faxs?file=index.js Надеюсь, вы сможете придумать более чистое решение.

 state = {
    searchInput: null,
    isVisable: false,
    clickedPrevState: false
  };

  render() {
    const getNewState = (prevState, event, value) => {
      console.log(prevState, event, value);
      let newState = { ...prevState, clickedPrevState: false };
      switch (event) {
        case 'input':
          newState.isVisable = value ? true : false;
          newState.searchInput = value;
          break;
        case 'clicked':
          if (prevState.searchInput) {
            newState.isVisable = true;
            newState.clickedPrevState = true; // used by next getNewState to determine if it was a click on input
          } else newState.isVisable = false;
          break;
        case 'change':
          if (prevState.searchInput amp;amp; (value || prevState.clickedPrevState)) {
            newState.isVisable = true;
          } else newState.isVisable = false;
          break;
        default:
          newState.isVisable = false;
      }
      return newState;
    };

    const menu = (
      <Menu>
        <Menu.Item key="1">Clicking me will not close the menu.</Menu.Item>
        <Menu.Item key="2">Clicking me will not close the menu also.</Menu.Item>
        <Menu.Item key="3">Clicking me will not close the menu.</Menu.Item>
      </Menu>
    );
    return (
      <Dropdown
        overlay={menu}
        trigger={['click']}
        visible={this.state.isVisable}
        onVisibleChange={flag =>
          this.setState(prevstate => {
            return getNewState(prevstate, 'change', flag);
          })
        }
        onClick={e =>
          this.setState(prevstate => {
            return getNewState(prevstate, 'clicked');
          })
        }
      >
        <Search
          value={this.state.searchInput}
          onInput={e =>
            this.setState(prevstate => {
              return getNewState(prevstate, 'input', e.target.value);
            })
          }
        />
      </Dropdown>
    );
}
 

Комментарии:

1. Я попробовал ту же логику в своем проекте, но сначала onVisibleChange запускается, а затем onClick событие (в данной песочнице кода порядок меняется на противоположный). Поэтому, когда я сначала нажимаю на поле поиска, событие изменения устанавливается в значение clickedPrevState false, а затем событие щелчка устанавливает состояние в значение true. Поэтому, если я нажму снаружи clickedPrevState , на данный момент это все еще верно, и выпадающий список не исчезнет. Если я нажму еще раз, он закроется. Есть ли возможность изменить порядок срабатывания onClick и onVisisbleChange событий?

2. Интересно, что это было бы наоборот. Это могут быть разные версии antd. Вам придется изменить логику, если это так. Просто глядя на это без тестирования, я думаю, что это должно сработать, просто удалив всю логику с помощью clickedPrevState. Когда я тестирую его, события, похоже, являются частью одного и того же цикла рендеринга, поэтому должны работать, не вызывая мерцания меню.