I have a very simple React App create using create-react-app. The app displays a single component passing in a value
and an onClick
callback and incrementshe value when the callback is triggered.
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;
The Block component takes a value
and a onClick
function and displays a square with the value in the centre and calls the onClick
whenever the square is clicked.
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;
This works excatly as described.
I have then extended this component to log the value on a mouseenter
and mouseleave
event by adding the relevent event handlers in a useEffect
hook called when the component mounts.
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;
The problem that I am having is that the two event handlers are closures so always print the value as it was when the component was mounted. I could rewrite the component as a class component and access the props via this but nothing else in the project uses class components. I could copy the value to a global variable but that would restrict me to a single instance.
What is the recommended way of accessing one or more props within a closure?
I should add that this was written to demonstrate the problem. In my app the value is a large object passed in and the div is a canvas with the data plotted onto it.
Advertisement
Answer
The easiest solution would be to use React synthetic events and remove the useEffect
:
return <div onMouseEnter={mouseEnter} onMouseLeave={mouseLeave} ... />