简体   繁体   English

使用事件监听器反应自定义下拉菜单

[英]React custom dropdown with event listener

I created a Dropdown that when I click outside of it the dropdown disappears. 我创建了一个Dropdown菜单,当我在其外部单击时,该下拉菜单将消失。 I used a click event listener to determine if I clicked outside the dropdown. 我使用点击事件监听器来确定是否在下拉列表之外单击。

After a few clicks, the page slows down and crashes. 单击几下后,页面变慢并崩溃。 Perhaps the state is being rendered in a loop or too many events are being fired at once? 也许状态是循环呈现的,或者一次触发了太多事件?

How do I fix this? 我该如何解决? Also, is there a more React way to determine if I clicked outside an element? 另外,还有更多的React方法来确定我是否在元素外部单击了吗? (Instead of using a document.body event listener) (而不是使用document.body事件侦听器)

Here is the codepen: 这是codepen:

 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> 

There's a pretty simple explanation for what you're experiencing. 对于您遇到的情况,有一个非常简单的解释。 :) :)

The way I was able to figure it out was the number of warnings that were showing up in the terminal every time I clicked somewhere was getting higher and higher, especially when the state changed . 我能够弄清楚的方式是,每次我单击某处时,在终端中显示的警告越来越多, 尤其是当状态更改时

The answer though is that since you were adding the event listener code in the render function, every time the code re-rendered it would add more and more event listeners slowing down your code. 但是,答案是,由于您是在render函数中添加事件侦听器代码,因此每次重新渲染代码时,都会添加越来越多的事件侦听器,从而降低了代码速度。

Basically the solution is that you should move the adding of event listeners to componentDidMount so it's only run once. 基本上,解决方案是将事件侦听器的添加内容移至componentDidMount以便仅运行一次。

Updated working javascript: 更新了工作的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')
  );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM