从功能组件中的闭包函数访问道具



我有一个非常简单的React应用程序,使用create-rect应用程序创建。应用程序显示传入valueonClick回调的单个组件,并在触发回调时增加值。

import React, { useState } from 'react';
import Block from './Block';
const App = () => {
const [count, setCount] = useState(0)
return (
<div className="App">
<Block
value={count}
onClick={ () => setCount(count + 1) }
/>
</div>
);
}
export default App;

Block组件采用valueonClick函数,并显示一个值位于中心的正方形,并在单击该正方形时调用onClick

const Block = ({ value, onClick }) => {
return (
<div
onClick={onClick}
style={{
width: 200,
height: 200,
margin: 50,
background: 'yellow',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 36,
}}
>
{value}
</div>
);
}
export default Block;

这与所描述的完全一样。

然后,我通过在组件装载时调用的useEffect挂钩中添加相关事件处理程序,扩展了该组件,以记录mouseentermouseleave事件的值。

import React, { useEffect, useRef } from 'react';

const Block = ({ value, onClick }) => {
const divRef = useRef();
const mouseEnter = () => {
console.log('mouse enter', value);
}
const mouseLeave = () => {
console.log('mouse leave', value);
}
useEffect(() => {
console.log('addEventListeners');
divRef.current.addEventListener('mouseenter', mouseEnter);
divRef.current.addEventListener('mouseleave', mouseLeave);
}, [])
return (
<div
ref={divRef}
onClick={onClick}
style={{
width: 200,
height: 200,
margin: 50,
background: 'yellow',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 36,
}}
>
{value}
</div>
);
}
export default Block;

我遇到的问题是,这两个事件处理程序都是闭包,所以总是打印组件安装时的值。我可以将组件重写为类组件,并通过this访问道具,但项目中没有其他组件使用类组件。我可以将值复制到一个全局变量,但这将限制我只能使用一个实例。

建议使用什么方法访问闭包中的一个或多个道具?

我应该补充一点,这是为了证明这个问题。在我的应用程序中,值是一个传入的大对象,div是一个画布,上面绘制了数据

我将使useCallback钩子创建的mouseEntermouseLeave函数具有value作为依赖项。useEffect也将具有这两个函数作为依赖项。

然后,您可以使用这样一个事实,即useEffect的返回函数在依赖关系更改时执行,就在调用新的useEffect之前。现在,在返回的函数中,只需删除侦听器,这样就总是只有两个侦听器。

它看起来像这样:

const mouseEnter = useCallback(() => {
console.log('mouse enter', value);
}, [value]);
const mouseLeave = useCallback(() => {
console.log('mouse leave', value);
}, [value]);
useEffect(() => {
console.log('addEventListeners');
divRef.current.addEventListener('mouseenter', mouseEnter);
divRef.current.addEventListener('mouseleave', mouseLeave);
return () => {
divRef.current.removeEventListener('mouseenter', mouseEnter);
divRef.current.removeEventListener('mouseenter', mouseEnter);
};
}, [mouseEnter, mouseLeave])

最简单的解决方案是使用React合成事件并删除useEffect:

return <div onMouseEnter={mouseEnter} onMouseLeave={mouseLeave} ... />

如果您想将MouseEnetr和MouseLeave事件添加到div元素中,您可以很好地实现这一点,如下所示。不需要任何裁判。

return (
<div
onClick={() => setValue(value + 1)}
onMouseEnter={() => mouseEnter(value)}
onMouseLeave={() => mouseLeave(value)}
style={{
width: 200,
height: 200,
margin: 50,
background: 'yellow',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 36,
}}
>
{value} 
</div>

如果你正在通过使用ref来探索同样的东西,那么,正如我所知,这可以实现,如下所示。

useEffect(() => {
console.log('addEventListeners');
// Remove listeners
divRef.current.removeEventListener('mouseenter', mouseEnter);
divRef.current.removeEventListener('mouseleave', mouseLeave);
// Add listeners
divRef.current.addEventListener('mouseenter', mouseEnter);
divRef.current.addEventListener('mouseleave', mouseLeave);
}, [value]) // Pass value as argument, then it behaves as componentDidUpdate

您需要使用useRef

const countRef = React.useRef(count);

用代替setCount

setCountRef = (value) => {
countRef.current = value;
setCount(value);
}

然后

const mouseLeave = () => {
console.log('mouse leave', countRef.current);
}

最新更新