Один из способов сделать это - использовать события React, и просто убедиться, что он более универсален. c.
Одна из проблем, с которыми вы столкнулись, заключается в том, что ссылка может ссылаться только на один узел за раз. И ref никогда не изменяет зависимости, поэтому useEffect запускается только один раз.
const { useState, useRef, useEffect, useCallback } = React;
function useHover() {
const [hovered, setHovered] = useState({});
const mouseOver = useCallback((event) => {
const target = event.target;
const key = target.getAttribute('data-key');
setHovered((curState) => ({ ...curState, [key]: true }));
}, []);
const mouseOut = useCallback((event) => {
const target = event.target;
const key = target.getAttribute('data-key');
setHovered((curState) => ({ ...curState, [key]: false }));
}, []);
return { mouseOver, mouseOut, hovered };
}
const object = { key1: 'test', key2: 'test2', key3: 'test3' };
const myObject = Object.entries(object);
const string = 'Header';
function App() {
const { mouseOver, mouseOut, hovered } = useHover();
const reactionItems = myObject.map(([key, value]) => (
<li key={key} data-key={key} onMouseOver={mouseOver} onMouseOut={mouseOut}>
{hovered[key]
? `${key} ${value.length > 1 ? 'x ' + value.length : ''}`
: `${key} ${value.length > 1 ? 'x ' + value.length : ''} ${value}`}
</li>
));
return (
<div className="App">
<h1>{string}</h1>
<h2>Reactions</h2>
<ul>{reactionItems}</ul>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
Вы также можете разделить элементы списка на их собственный компонент, который позволит вам работать с useHover более близко к тому, как у вас это было.