我面临一个问题,有一个<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;