useState 变量未在 chrome 调试器中更新,但在 ReactDevTools 中更新



我正在尝试运行带有轮子事件的数组。我有一个useState钩子,当安装组件时,它会以值0初始化,当我在触控板上滑动时,如果我向下滑动,状态计数会递减1,反之亦然。此值角色表示当前索引位置。

一切都很好,活动和状态如我所料。问题是,当我试图做一些条件来控制数组的位置不大于或小于这段代码中数组的大小时:

if (Math.sign(e.deltaY) === -1) {
console.log('swipe DOWN');
if (persona > 0) {
setPersona(prevPers => prevPers - 1);
}
} else {
console.log('swipe UP');
if (persona <= team.length) {
setPersona(prevPers => prevPers + 1);
}
}

当我在Chrome调试器中观察角色值时,该值始终等于0,并且由于这个条件:

if (persona > 0) {
setPersona(prevPers => prevPers - 1);
}

该值始终递增,因为persona值始终为0。然而,当我用ReactDevTools观看角色值时,如果我触摸轮子,值就会发生变化。

这是我的代码:

export const Equipo = () => {
const container = useRef(null);
const [persona, setPersona] = useState(0);
const handleWheel = e => {
e.stopPropagation();
setEvent(false);
if (Math.sign(e.deltaY) === -1) {
console.log('swipe DOWN');
if (persona > 0) {
setPersona(prevPers => prevPers - 1);
}
} else {
console.log('swipe UP');
if (persona <= team.length) {
setPersona(prevPers => prevPers + 1);
}
}
setTimeout(() => {
console.log('available to scroll');
setEvent(true);
}, 3000);
};
const setEvent = status => {
status === true
? container.current.addEventListener('wheel', handleWheel, false)
: container.current.removeEventListener('wheel', handleWheel, false);
};
useEffect(() => {
setEvent(true);
}, []);
return (
<div
ref={container}
className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
>
<div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
{team[persona]}
</div>
</div>
);
};
export default Equipo;

我不知道为什么Chrome Debugger中的值与ReactDevTools中的值不同。这是我去bug时的值:Chrome调试器中的Persona值正如我们所看到的,角色控制台日志是2,但悬停时仍然打印0。

这就是ReactDevTools中人物角色在同一点上的价值:ReactDevTools 中的值相同

我不知道我是否遗漏了一些非常明显的东西,但我不明白为什么在这个条件下,人物角色的值仍然是0

因此,setEvent函数中的角色值始终为0的原因是,它周围有一个闭包,该闭包是在组件首次装载和useEffect钩子运行时创建的。

以下是我将如何以与您当前相同的方式解决您的问题:

我将handleWheel和setEvent函数移到了useEffect函数中,以表明它们的值在效果的生命周期内不会改变。我还去掉了对persona当前值的引用,这样setPersona调用就不会超出它们的界限。

export const Equipo = () => {
const container = useRef(null);
const [persona, setPersona] = useState(0);
useEffect(() => {
const handleWheel = (e) => {
e.stopPropagation();
setEvent(false);
if (Math.sign(e.deltaY) === -1) {
console.log('swipe DOWN');
setPersona((prevPers) => {
Math.max(0, prevPers - 1);
});
} else {
console.log('swipe UP');
setPersona((prevPers) => Math.min(team.length, prevPers + 1));
}
setTimeout(() => {
console.log('available to scroll');
setEvent(true);
}, 3000);
};
const setEvent = (status) => {
status === true
? container.current.addEventListener('wheel', handleWheel, false)
: container.current.removeEventListener('wheel', handleWheel, false);
};
setEvent(true);
return () => {
setEvent(false);
};
}, []);
return (
<div
ref={container}
className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
>
<div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
{team[persona]}
</div>
</div>
);
};
export default Equipo;

这是我使用节流函数而不是超时的另一种方法。我相信这种方式更干净,因为你不需要担心裁判和暂停。这也是确保函数只在每t段时间运行一次的更正常的方法。

与其自己绑定事件侦听器,不如让react来处理它

限制代码本身来自https://blog.bitsrc.io/understanding-throttling-and-debouncing-973131c1ba07.尽管你可以很容易地使用lodash的油门功能。

export const Equipo = () => {
const [persona, setPersona] = useState(0);
const handleWheel = useCallback(
throttle((e) => {
e.stopPropagation();
if (Math.sign(e.deltaY) === -1) {
console.log('swipe DOWN');
setPersona((prevPers) => {
Math.max(0, prevPers - 1);
});
} else {
console.log('swipe UP');
setPersona((prevPers) => Math.min(team.length, prevPers + 1));
}
}, 3000),
[team.length]
);

return (
<div
onWheel={handleWheel}
className="bg-white h-screen w-full p-header pb-4 px-4 h-full flex justify-center align-middle items-center"
>
<div className="flex flex-col justify-center items-center h-p70 w-10/12 bg-red-500 overflow-hidden">
{team[persona]}
</div>
</div>
);
};
export default Equipo;
function throttle(f, t) {
let lastCall;
return function (args) {
let previousCall = lastCall;
lastCall = Date.now();
if (
previousCall === undefined || // function is being called for the first time
lastCall - previousCall > t
) {
// throttle time has elapsed
f(args);
}
};
}

解释为什么人物角色总是0:

在Javascript中,当创建一个函数时,它会创建一个所谓的闭包,在这里它会拉入其范围内存在的所有变量,并允许函数始终能够访问这些变量。闭合上的MDN

实际情况是,当useEffect在装载时运行时,它会拉入setEvent函数的当前版本,并且可以访问handleWheel函数的当前版。setEvent会将handleWheel的原始版本绑定到div,并且永远不会有机会更新绑定的handleWheel的版本。由于闭包,当persona为0时,最初的handleWheel函数在persona周围有一个闭包。由于每次重新渲染时都会有一个新的角色变量实例,因此封闭变量从未更新。

本质上,这就是您的代码在useEffect中的样子。希望这能帮助你了解为什么你会遇到问题。

useEffect(() => {
const setEvent = (status) => {
const handleWheel = (e) => {
e.stopPropagation();
setEvent(false);
if (Math.sign(e.deltaY) === -1) {
console.log('swipe DOWN');
if (persona > 0) {
setPersona((prevPers) => prevPers - 1);
}
} else {
console.log('swipe UP');
if (persona <= team.length) {
setPersona((prevPers) => prevPers + 1);
}
}
setTimeout(() => {
console.log('available to scroll');
setEvent(true);
}, 3000);
};
status === true
? container.current.addEventListener('wheel', handleWheel, false)
: container.current.removeEventListener('wheel', handleWheel, false);
};
setEvent(true);
}, []);

相关内容

最新更新