我创建了一个下拉列表
,当我在其外部单击时,下拉列表就会消失。我使用单击事件监听器来确定我是否在下拉列表外部单击。
点击几次后,页面速度变慢并崩溃。也许状态正在循环中呈现,或者一次触发了太多事件?
我该如何解决这个问题? 另外,是否有更多的 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/