为什么我们要在没有依赖数组的情况下使用 useEffect?

dag*_*da1 29 javascript reactjs react-hooks

我有这个代码:

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);
Run Code Online (Sandbox Code Playgroud)

这将在每个渲染上添加和删除一个事件处理程序。

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

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

nem*_*035 21

对于这种确切的情况,您是对的,因为undefined作为useEffect.

这意味着useEffect在每个渲染上运行,因此事件处理程序将不必要地在每个渲染上分离和重新附加。

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'))
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

但是,如果您通过传入一个空数组来明确声明没有依赖关系[]useEffect则只会运行一次,从而使这种模式对于事件处理程序附件来说是完全合法的。

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'))
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

  • “..事件处理程序将不必要地在每次渲染时分离和重新连接。”。是不是就好像我们写了一段没有任何hook的代码一样。例如,假设我们有赋值 (const var = "something"),在每个渲染上我们都会让该赋值工作,就像我们有 useEffect(() =&gt; const var = "something") 一样。请不要考虑变量范围 (3认同)
  • 那么为什么要在没有依赖数组的情况下使用 useEffect 呢? (3认同)
  • @nem 在这种情况下,你不能只在函数中声明该功能而不使用 useEffect 吗? (3认同)
  • @dwjohnston 好吧,您可以将它用于需要在每个渲染上运行的任何操作。具体是什么操作取决于您正在构建的应用程序。 (2认同)
  • 这取决于用例。渲染周期内的同步函数调用将在渲染内运行,并且可能会影响性能。效果总是在渲染之后运行,因此至少对用户体验的影响较小,因为在效果开始运行时屏幕已经显示了应显示的内容。 (2认同)