reactjs - useEffect 没有正确删除事件
问题描述
我面临一个问题,有一个<App/>
组件呈现一个子组件,即<Button/>
组件。该<Button/>
组件有 2props
个,一个是布尔值,另一个是函数。当用户单击按钮时将显示背景,背景只是div
在用户单击时隐藏。
当 boolean 属性发生变化时,<Button/>
组件会动态添加事件侦听器,并在用户单击文档上的任意位置时移除侦听器。
我用基于类的生命周期钩子成功地添加了这个功能,但是在用 React 钩子替换组件时遇到了问题。
我知道有一个名为的钩子useEffect
可以替换 的行为,componentDidMount
但是我的实现不是基于更改的道具删除侦听器。即使隐藏了背景,听众也无需单击按钮即可工作,以及为什么 linter 会抱怨。componentWillUnmount
componentDidUpdate
useEffect
React Hook useEffect 缺少依赖项:“addEvents”和“removeEvents”。要么包含它们,要么删除依赖数组。(反应钩子/详尽的deps)。
如何修复此行为并使该组件像类<Button />
组件一样?
类组件的功能:
// CSS styles for backdrop
const customStyles = {
backgroundColor: "rgba(0,0,0,.5)",
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0
};
// Button class component
class Button extends React.Component {
// Return the function
toggle = event => {
return this.props.toggle(event);
};
// Attach Listener to the document object
handleDocumentClick = event => {
this.toggle(event);
};
// Add listeners to the document object
addEvents = () => {
["click", "touchstart"].forEach(event =>
// Event propogate from body(root) element to eventTriggered element.
document.addEventListener(event, this.handleDocumentClick, true)
);
};
// remove listeners from the document object
removeEvents = () => {
["click", "touchstart"].forEach(event =>
document.removeEventListener(event, this.handleDocumentClick, true)
);
};
// Add or remove listeners when the prop changes
manageProp = () => {
if (this.props.open) {
this.addEvents();
} else {
this.removeEvents();
}
};
componentDidMount() {
this.manageProp();
}
componentWillUnmount() {
alert("Button cleanup");
this.removeEvents();
}
componentDidUpdate(prevProps) {
if (this.props.open !== prevProps.open) {
this.manageProp();
}
}
render() {
return <button onClick={this.toggle}>Button</button>;
}
}
// App class component
class App extends React.Component {
state = {
open: false
};
toggle = () => {
alert("Button is clicked");
this.setState({
open: !this.state.open
});
};
render() {
return (
<div className="app">
{/* Class-based Button Component */}
<Button open={this.state.open} toggle={this.toggle} />
{/* Backdrop */}
{this.state.open && <div style={customStyles} />}
</div>
);
}
}
// Render it
ReactDOM.render(<App />, document.getElementById("root"));
<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"></div>
带有反应钩子的组件:
import React, { useEffect } from "react";
const Button = props => {
const toggle = event => {
return props.toggle(event);
};
// Attach this to the document object
const handleDocumentClick = event => {
toggle(event);
};
// Add event listeners
const addEvents = () => {
["click", "touchstart"].forEach(event =>
document.addEventListener(event, handleDocumentClick, true)
);
};
// Remove event listeners
const removeEvents = () => {
["click", "touchstart"].forEach(event =>
document.removeEventListener(event, handleDocumentClick, true)
);
};
// Add or remove listeners based on the state changes
const manageProp = () => {
if (props.open) {
addEvents();
} else {
removeEvents();
}
};
// Mount, Unmount & DidUpdate
useEffect(() => {
manageProp();
// Unmount
return () => {
alert("Button cleanup");
removeEvents();
};
}, [props.open]);
// Render it
return <button onClick={toggle}>Button</button>;
};
export default Button;
解决方案
useEffect
每次任何依赖项更改时返回的函数。在您的情况下,该依赖项是props.open
.
当您removeEvents
从该函数调用时,您将由于为props.open
真而添加两次事件。相反,您可以做的是在每次道具更改时删除事件:
useEffect(() => {
// Add or remove listeners based on the state changes
const manageProp = () => {
if (props.open) {
addEvents();
} else {
removeEvents();
}
};
// mount
manageProp();
// unmount
return () => {
removeEvents();
};
}, [props.open]);
只需移动addEvents
并removeEvents
进入useEffect
即可摆脱警告
推荐阅读
- android - 我怎样才能像图片上一样创建用户界面
- javascript - 如何处理firestore中为空的数据?
- c - 在 cmd 上打印到 stderr 无法打印非 ASCII UTF-8 文本的第一个字符
- python - 如何存储来自 Web 请求响应的特定数据?
- android - 使用 BottomSheetDialogFragment 删除底部表中的暗淡背景
- graphql - 链接资产随机消失
- html - 有没有办法隐藏检查元素的评论?
- sql - 使用 SQL Server 2016 解析 XML
- javascript - 如何在类外的另一个函数中使用类的方法?
- pytorch - 如何在 Pytorch 中训练 LSTM?