gpt4 book ai didi

reactjs - 使用 React Hooks,为什么我的事件处理程序会以错误的状态触发?

转载 作者:行者123 更新时间:2023-12-02 21:13:39 25 4
gpt4 key购买 nike

我正在尝试使用 React hooks 创建这个旋转 div 示例的副本。 https://codesandbox.io/s/XDjY28XoV

这是我到目前为止的代码

import React, { useState, useEffect, useCallback } from 'react';

const App = () => {
const [box, setBox] = useState(null);

const [isActive, setIsActive] = useState(false);
const [angle, setAngle] = useState(0);
const [startAngle, setStartAngle] = useState(0);
const [currentAngle, setCurrentAngle] = useState(0);
const [boxCenterPoint, setBoxCenterPoint] = useState({});

const setBoxCallback = useCallback(node => {
if (node !== null) {
setBox(node)
}
}, [])

// to avoid unwanted behaviour, deselect all text
const deselectAll = () => {
if (document.selection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
}

// method to get the positionof the pointer event relative to the center of the box
const getPositionFromCenter = e => {
const fromBoxCenter = {
x: e.clientX - boxCenterPoint.x,
y: -(e.clientY - boxCenterPoint.y)
};
return fromBoxCenter;
}

const mouseDownHandler = e => {
e.stopPropagation();
const fromBoxCenter = getPositionFromCenter(e);
const newStartAngle =
90 - Math.atan2(fromBoxCenter.y, fromBoxCenter.x) * (180 / Math.PI);
setStartAngle(newStartAngle);
setIsActive(true);
}

const mouseUpHandler = e => {
deselectAll();
e.stopPropagation();
if (isActive) {
const newCurrentAngle = currentAngle + (angle - startAngle);
setIsActive(false);
setCurrentAngle(newCurrentAngle);
}
}

const mouseMoveHandler = e => {
if (isActive) {
const fromBoxCenter = getPositionFromCenter(e);
const newAngle =
90 - Math.atan2(fromBoxCenter.y, fromBoxCenter.x) * (180 / Math.PI);
box.style.transform =
"rotate(" +
(currentAngle + (newAngle - (startAngle ? startAngle : 0))) +
"deg)";
setAngle(newAngle)
}
}

useEffect(() => {
if (box) {
const boxPosition = box.getBoundingClientRect();
// get the current center point
const boxCenterX = boxPosition.left + boxPosition.width / 2;
const boxCenterY = boxPosition.top + boxPosition.height / 2;

// update the state
setBoxCenterPoint({ x: boxCenterX, y: boxCenterY });
}

// in case the event ends outside the box
window.onmouseup = mouseUpHandler;
window.onmousemove = mouseMoveHandler;
}, [ box ])

return (
<div className="box-container">
<div
className="box"
onMouseDown={mouseDownHandler}
onMouseUp={mouseUpHandler}
ref={setBoxCallback}
>
Rotate
</div>
</div>
);
}

export default App;

当前,即使状态实际上为 true,mouseMoveHandler 也会以 isActive = false 的状态调用。我怎样才能让这个事件处理程序以正确的状态触发?

此外,控制台正在记录警告:

React Hook useEffect has missing dependencies: 'mouseMoveHandler' and 'mouseUpHandler'. Either include them or remove the dependency array  react-hooks/exhaustive-deps

为什么我必须在 useEffect 依赖数组中包含组件方法?我从未需要使用 React Hooks 对其他更简单的组件执行此操作。

谢谢

最佳答案

问题

Why is isActive false?

const mouseMoveHandler = e => {
if(isActive) {
// ...
}
};

(为了方便起见,我只讨论 mouseMoveHandler ,但这里的所有内容也适用于 mouseUpHandler )

当上面的代码运行时,会创建一个函数实例,该实例会拉入 isActive变量通过 function closure 。该变量是一个常量,因此如果 isActive当函数定义时为 false,那么它总是将是 false只要该函数实例存在。

useEffect还需要一个函数,并且该函数有一个对您的 moveMouseHandler 的常量引用函数实例 - 只要 useEffect 回调存在,它就会引用 moveMouseHandler 的副本哪里isActive是假的。

何时 isActive更改、组件重新渲染以及 moveMouseHandler 的新实例将在其中创建 isActivetrue 。然而,useEffect仅当依赖项发生更改时才重新运行其函数 - 在本例中,依赖项 ( [box] ) 未更改,因此 useEffect不重新运行且版本为moveMouseHandler哪里isActive为 false 时仍附加到窗口,无论当前状态如何。

这就是为什么“exhaustive-deps”钩子(Hook)警告您 useEffect - 它的一些依赖项可以更改,而不会导致 Hook 重新运行并更新这些依赖项。

<小时/>

修复它

由于钩子(Hook)间接依赖于 isActive ,您可以通过添加 isActive 来解决此问题到deps useEffect 的数组:

// Works, but not the best solution
useEffect(() => {
//...
}, [box, isActive])

但是,这不是很干净:如果您更改 mouseMoveHandler所以它取决于更多的状态,你会遇到同样的错误,除非你记得把它添加到 deps数组也是如此。 (linter 也不会喜欢这样)

useEffect函数间接依赖于isActive因为它直接取决于mouseMoveHandler ;因此,您可以将其添加到依赖项中:

useEffect(() => {
//...
}, [box, mouseMoveHandler])

通过此更改,useEffect 将使用新版本的 mouseMoveHandler 重新运行。这意味着它将尊重 isActive但是它会运行得太频繁 - 它每次都会运行 mouseMoveHandler成为一个新的函数实例...这是每个渲染,因为每个渲染都会创建一个新函数。

我们并不需要每次渲染都创建一个新函数,只有当isActive时才需要创建一个新函数。已更改:React 提供了 useCallback该用例的钩子(Hook)。您可以定义您的mouseMoveHandler

const mouseMoveHandler = useCallback(e => {
if(isActive) {
// ...
}
}, [isActive])

现在只有在 isActive 时才会创建新的函数实例更改,然后将触发 useEffect在适当的时刻运行,您可以更改 mouseMoveHandler 的定义(例如添加更多状态)而不破坏您的 useEffect钩子(Hook)。

<小时/>

这可能仍然会给您的 useEffect 带来问题hook:每次都会重新运行isActive变化,这意味着它每次都会设置框中心点 isActive更改,这可能是不需要的。您应该将效果分成两个单独的效果以避免此问题:

useEffect(() => {
// update box center
}, [box])

useEffect(() => {
// expose window methods
}, [mouseMoveHandler, mouseUpHandler]);
<小时/>

最终结果

最终您的代码应如下所示:

const mouseMoveHandler = useCallback(e => {
/* ... */
}, [isActive]);

const mouseUpHandler = useCallback(e => {
/* ... */
}, [isActive]);

useEffect(() => {
/* update box center */
}, [box]);

useEffect(() => {
/* expose callback methods */
}, [mouseUpHandler, mouseMoveHandler])

更多信息:

Dan Abramov,React 作者之一,在他的 Complete Guide to useEffect 中详细介绍了更多细节。博文。

关于reactjs - 使用 React Hooks,为什么我的事件处理程序会以错误的状态触发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55874789/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com