为什么我们要在没有依赖数组的情况下使用 useEffect? [英] Why would we use useEffect without a dependency array?
问题描述
我有这个代码:
const App: React.FC = () =>{const [isOpen, setIsOpen] = React.useState(true);const [maxHeight, setMaxHeight] = React.useState();const wrapper = React.useRef(null);const content = React.useRef(null);const setElementMaxHeight = () =>{如果(内容&& content.current){setMaxHeight(isOpen ? content.current.offsetHeight : 0);}};useEffect(() => {setElementMaxHeight();window.addEventListener("resize", setElementMaxHeight);返回 () =>{window.removeEventListener("resize", setElementMaxHeight);};});const 切换 = () =>{setIsOpen(!isOpen);};返回 (<div><button onClick={toggle}><span className="nominal-result__expander fa"/>按钮>
);};const rootElement = document.getElementById("root");ReactDOM.render(
这将在每次渲染时添加和删除一个事件处理程序.
这一定是坏事吗,这真的从成为一个钩子中获得了什么吗?
这是在代码审查中提出的,我说它很糟糕,因为它在每次渲染时添加和删除了事件侦听器.
对于这个 exact 情况你是对的,因为 undefined
作为 的依赖项被传递使用效果
.
这意味着 useEffect
在每次渲染上运行,因此事件处理程序将在每次渲染时不必要地分离和重新附加.
function listener() {console.log('点击');}函数示例(){const [count, setCount] = window.React.useState(0);window.React.useEffect(() => {console.log(`添加监听器 ${count}`);window.addEventListener("点击", 监听器);返回 () =>{console.log(`移除监听器 ${count}`);window.removeEventListener("点击", 监听器);};});//<-- 因为我们没有在这里传递任何东西,所以我们对每个渲染都有影响window.React.useEffect(() => {setTimeout(() => {设置计数(计数 + 1);}, 1000)});返回计数;}window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><div id="root"></div>
但是如果您通过传入一个空数组 []
明确声明没有依赖关系,useEffect
将只运行一次,从而使该模式对于事件处理程序附件完全合法.
function listener() {console.log('点击');}函数示例(){const [count, setCount] = window.React.useState(0);window.React.useEffect(() => {console.log(`添加监听器 ${count}`);window.addEventListener("点击", 监听器);返回 () =>{console.log(`移除监听器 ${count}`);window.removeEventListener("点击", 监听器);};}, []);//<-- 我们可以控制这个效果在这个组件的生命周期内只运行一次window.React.useEffect(() => {setTimeout(() => {设置计数(计数 + 1);}, 1000)});返回计数;}window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><div id="root"></div>
I have this code:
const App: React.FC = () => {
const [isOpen, setIsOpen] = React.useState(true);
const [maxHeight, setMaxHeight] = React.useState();
const wrapper = React.useRef<HTMLDivElement>(null);
const content = React.useRef<HTMLDivElement>(null);
const setElementMaxHeight = () => {
if (content && content.current) {
setMaxHeight(isOpen ? content.current.offsetHeight : 0);
}
};
useEffect(() => {
setElementMaxHeight();
window.addEventListener("resize", setElementMaxHeight);
return () => {
window.removeEventListener("resize", setElementMaxHeight);
};
});
const toggle = () => {
setIsOpen(!isOpen);
};
return (
<div>
<button onClick={toggle}>
<span className="nominal-result__expander fa" />
</button>
<div
className="nominal-results__list-wrapper"
ref={wrapper}
style={!!maxHeight ? { maxHeight: `${maxHeight}px` } : undefined }
>
<div className="nominal-results__list" ref={content} />
</div>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This will add and remove an event handler on each render.
Is this necessarily bad and does this actually gain anything from being a hook?
This came up in a code review and I am saying it is bad because it adds and removes the event listener on every render.
For this exact case you're right because undefined
is passed as the dependencies of useEffect
.
This means useEffect
runs on every render and thus the event handlers will unnecessarily get detached and reattached on each render.
function listener() {
console.log('click');
}
function Example() {
const [count, setCount] = window.React.useState(0);
window.React.useEffect(() => {
console.log(`adding listener ${count}`);
window.addEventListener("click", listener);
return () => {
console.log(`removing listener ${count}`);
window.removeEventListener("click", listener);
};
}); // <-- because we're not passing anything here, we have an effect on each render
window.React.useEffect(() => {
setTimeout(() => {
setCount(count + 1);
}, 1000)
});
return count;
}
window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
But if you explicitly declare no dependencies by passing in an empty array []
, useEffect
will only run once, thus making this pattern perfectly legitimate for event handler attachment.
function listener() {
console.log('click');
}
function Example() {
const [count, setCount] = window.React.useState(0);
window.React.useEffect(() => {
console.log(`adding listener ${count}`);
window.addEventListener("click", listener);
return () => {
console.log(`removing listener ${count}`);
window.removeEventListener("click", listener);
};
}, []); // <-- we can control for this effect to run only once during the lifetime of this component
window.React.useEffect(() => {
setTimeout(() => {
setCount(count + 1);
}, 1000)
});
return count;
}
window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
这篇关于为什么我们要在没有依赖数组的情况下使用 useEffect?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!