[英]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.