为什么我们要在没有依赖数组的情况下使用 useEffect? [英] Why would we use useEffect without a dependency array?

查看:16
本文介绍了为什么我们要在没有依赖数组的情况下使用 useEffect?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个代码:

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(, rootElement);

这将在每次渲染时添加和删除一个事件处理程序.

这一定是坏事吗,这真的从成为一个钩子中获得了什么吗?

这是在代码审查中提出的,我说它很糟糕,因为它在每次渲染时添加和删除了事件侦听器.

解决方案

对于这个 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆