javascript - 使用事件监听器响应自定义下拉列表

标签 javascript reactjs dropdown addeventlistener

我创建了一个下拉列表,当我在其外部单击时,下拉列表就会消失。我使用单击事件监听器来确定我是否在下拉列表外部单击。

点击几次后,页面速度变慢并崩溃。也许状态正在循环中呈现,或者一次触发了太多事件?

我该如何解决这个问题? 另外,是否有更多的 React 方法来确定我是否在元素外部单击? (而不是使用 document.body 事件监听器)

这是代码笔:

const items = [
    {
        value: 'User1'
    },
    {
        value: 'User2'
    },
    {
        value: 'User3'
    },
    {
        value: 'User4'
    },
    {
        value: 'User5'
    }
];
    
class Dropdown extends React.Component {
  state = {
    isActive: false,
  }

  render() {
    const { isActive } = this.state;
    document.addEventListener('click', (evt) => {
        if (evt.target.closest('#dropdownContent')) {
          //console.warn('clicked inside target do nothing');
          return;
        }

        if (evt.target.closest('#dropdownHeader')) {
          //console.warn('clicked the header toggle');
          this.setState({isActive: !isActive});
        }

        //console.warn('clicked outside target');
        if (isActive) {
          this.setState({isActive: false});
        }
      });
    
      return (
          <div id="container">
              <div id="dropdownHeader">select option</div>
              {isActive && (
                <div id="dropdownContent">
                  {items.map((item) => (
                    <div id="item" key={item.value}>
                      {item.value}
                    </div>
                  ))}
                </div>
              )}
          </div>
      );
  };
}
  ReactDOM.render(
    <Dropdown items={items} />,
    document.getElementById('root')
  );
#container {
    position: relative;
    height: 250px;
    border: 1px solid black;
}

#dropdownHeader {
    width: 100%;
    max-width: 12em;
    padding: 0.2em 0 0.2em 0.2em;
    margin: 1em;
    cursor: pointer;
    box-shadow: 0 1px 4px 3px rgba(34, 36, 38, 0.15);
}

#dropdownContent {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 3em;
    width: 100%;
    max-width: 12em;
    margin-left: 1em;
    box-shadow: 0 1px 4px 0 rgba(34, 36, 38, 0.15);
    padding: 0.2em;
}

#item {
    font-size: 12px;
    font-weight: 500;
    padding: 0.75em 1em 0.75em 2em;
    cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root">
    <!-- This element's contents will be replaced with your component. -->
</div>

最佳答案

对于您所经历的情况有一个非常简单的解释。 :)

我发现这个问题的方法是,每次我点击某个地方时,终端中显示的警告数量越来越多,尤其是当状态发生变化时。 p>

答案是,由于您在渲染函数中添加了事件监听器代码,因此每次重新渲染代码时,都会添加越来越多的事件监听器,从而降低代码速度。

基本上,解决方案是您应该将事件监听器的添加移至 componentDidMount,以便它仅运行一次。

更新了工作 JavaScript:

    const items = [
        {
            value: 'User1'
        },
        {
            value: 'User2'
        },
        {
            value: 'User3'
        },
        {
            value: 'User4'
        },
        {
            value: 'User5'
        }
    ];

class Dropdown extends React.Component {
  state = {
    isActive: false,
  }

  // added component did mount here
  componentDidMount(){
    const { isActive } = this.state;
    document.addEventListener('click', (evt) => {
        if (evt.target.closest('#dropdownContent')) {
          console.warn('clicked inside target do nothing');
          return;
        }

        if (evt.target.closest('#dropdownHeader')) {
          console.warn('clicked the header toggle');
          this.setState({isActive: !isActive});
        }

        console.warn('clicked outside target');
        if (isActive) {
          this.setState({isActive: false});
        }
      });
  }

  render() {
    const { isActive } = this.state;
    //removed event listener here
      return (
          <div id="container">
              <div id="dropdownHeader">select option</div>
              {isActive && (
                <div id="dropdownContent">
                  {items.map((item) => (
                    <div id="item" key={item.value}>
                      {item.value}
                    </div>
                  ))}
                </div>
              )}
          </div>
      );
  };
}
  ReactDOM.render(
    <Dropdown items={items} />,
    document.getElementById('root')
  );

关于javascript - 使用事件监听器响应自定义下拉列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57634502/

相关文章:

css - 悬停在 CSS 下拉菜单上无法正常工作

javascript - AngularJS ng-下拉树结构的模型定义

html - 如何使用html和css使div与另一个div重叠

javascript 日期增量

javascript - 如何禁用用户 Firebase?

javascript - 在 react 渲染中使用异步和等待

reactjs - 如何计算数组值

javascript - 调用 $location.url 后无法访问 obj

javascript - angularjs textarea - 如何使其与 angularjs 下拉列表中选定的值保持同步

javascript - backbone.js - 事件,知道点击了什么