这是我的情况:
我有一个自定义 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/