javascript - 哪个性能更好 : add and remove event listener on every render VS running an useEffect to update a ref

标签 javascript reactjs react-hooks

这是我的情况:

我有一个自定义 Hook ,称为 useClick,它获取一个 HTML 元素 和一个 callback 作为输入,附加一个 点击事件监听器到该元素,并将回调设置为事件处理程序

App.js

function App() {
  const buttonRef = useRef(null);
  const [myState, setMyState] = useState(0);

  function handleClick() {
    if (myState === 3) {
      console.log("I will only count until 3...");
      return;
    }
    setMyState(prevState => prevState + 1);
  }

  useClick(buttonRef, handleClick);

  return (
    <div>
      <button ref={buttonRef}>Update counter</button>
      {"Counter value is: " + myState}
    </div>
  );
}

useClick.js

import { useEffect } from "react";

function useClick(element, callback) {
  console.log("Inside useClick...");

  useEffect(() => {
    console.log("Inside useClick useEffect...");
    const button = element.current;

    if (button !== null) {
      console.log("Attaching event handler...");
      button.addEventListener("click", callback);
    }
    return () => {
      if (button !== null) {
        console.log("Removing event handler...");
        button.removeEventListener("click", callback);
      }
    };
  }, [element, callback]);
}

export default useClick;

请注意,对于上面的代码,我将在每次调用此 Hook 时添加和删除事件监听器(因为回调,handleClick 在每个渲染)。它必须改变,因为它取决于 myState 变量,该变量在每次渲染时都会改变。

而且我非常希望只在挂载时添加事件监听器并在卸载时删除。而不是在每次调用时添加和删除。


在 SO 上,有人建议我可以使用以下内容:

useClick.js

function useClick(element, callback) {
    console.log('Inside useClick...');

    const callbackRef = useRef(callback);

    useEffect(() => {
        callbackRef.current = callback;
    }, [callback]);

    const callbackWrapper = useCallback(props => callbackRef.current(props), []);

    useEffect(() => {
        console.log('Inside useClick useEffect...');
        const button = element.current;

        if (button !== null) {
            console.log('Attaching event handler...');
            button.addEventListener('click', callbackWrapper);
        }
        return () => {
            if (button !== null) {
                console.log('Removing event handler...');
                button.removeEventListener('click', callbackWrapper);
            }
        };
    }, [element, callbackWrapper]);
}

问题

它按预期工作。它只在挂载时添加事件监听器,并在卸载时将其删除。

上面的代码使用了一个回调包装器,它使用了一个在渲染中保持相同的 ref(所以我可以将它用作事件处理程序并且只安装一次),以及它的 .current 属性,它通过 useEffect Hook 在每次渲染时使用新的回调进行更新。

问题是在性能方面,哪种方法最好?运行 useEffect() Hook 是否比在每次渲染时添加和删除事件监听器更便宜?

无论如何我可以测试这个吗?

最佳答案

App.js

function App() {
  const buttonRef = useRef(null);
  const [myState, setMyState] = useState(0);

  // handleClick remains unchanged
  const handleClick = useCallback(
    () => setMyState(prevState => prevState >= 3 ? 3 : prevState + 1),
    []
  );

  useClick(buttonRef, handleClick);

  return (
    <div>
      <button ref={buttonRef}>Update counter</button>
      {"Counter value is: " + myState}
    </div>
  );
}

更专业的回答:

App.js

function App() {
  const buttonRef = useRef(null);
  const [myState, handleClick] = useReducer(
    prevState => prevState >= 3 ? 3 : prevState + 1,
    0
  );

  useClick(buttonRef, handleClick);

  return (
    <div>
      <button ref={buttonRef}>Update counter</button>
      {"Counter value is: " + myState}
    </div>
  );
}

关于javascript - 哪个性能更好 : add and remove event listener on every render VS running an useEffect to update a ref,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56236043/

相关文章:

javascript - 克隆对象数组失败

javascript - 链接无法在新窗口中打开

javascript - 对象作为 React 子对象无效(找到 : [object Promise]) Google Maps

javascript - React Select 设置错误的值

javascript - 鼠标悬停在 ag 网格上时行突出显示 react

javascript - "useRefs"变量是 div 引用的初始值?

javascript - 如何使用jQuery检查选择框中的任何选项是否等于另一个选择框中的任何选项

javascript - React useEffect 如何监视和更新状态?

reactjs - 尽管依赖项没有变化,useEffect 仍运行无限循环

javascript - 从 props 打印值,该值通过 mapStateToProps 从 redux 传递到组件