[英]React custom dropdown with event listener
我创建了一个Dropdown
菜单,当我在其外部单击时,该下拉菜单将消失。 我使用点击事件监听器来确定是否在下拉列表之外单击。
单击几下后,页面变慢并崩溃。 也许状态是循环呈现的,或者一次触发了太多事件?
我该如何解决? 另外,还有更多的React方法来确定我是否在元素外部单击了吗? (而不是使用document.body事件侦听器)
这是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>
对于您遇到的情况,有一个非常简单的解释。 :)
我能够弄清楚的方式是,每次我单击某处时,在终端中显示的警告越来越多, 尤其是当状态更改时 。
但是,答案是,由于您是在render函数中添加事件侦听器代码,因此每次重新渲染代码时,都会添加越来越多的事件侦听器,从而降低了代码速度。
基本上,解决方案是将事件侦听器的添加内容移至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')
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.