How can I assign a new ref to every iteration insi

2020-04-19 19:30发布

I'm not sure how to ask this question, because I'm still unable to accurately frame the problem.

I've created a useHover function. Below, you'll see that I am mapping over data and rendering a bunch of photos. However, the useHover only works on the first iteration.

I suspect that it's because of my ref. How does this work? Should I creating a new ref inside of each iteration -- or is that erroneous thinking..?

How can I do this?

Here's my useHover function.

const useHover = () => {
  const ref = useRef();
  const [hovered, setHovered] = useState(false);

  const enter = () => setHovered(true);
  const leave = () => setHovered(false);

  useEffect(() => {
    ref.current.addEventListener("mouseenter", enter);
    ref.current.addEventListener("mouseleave", leave);

    return () => {
      ref.current.removeEventListener("mouseenter", enter);
      ref.current.removeEventListener("mouseleave", leave);
    };
  }, [ref]);

  return [ref, hovered];
};

And here's my map function. As you can see I've assigned the ref to the image.

The problem: Only one of the images works when hovered.

const [ref, hovered] = useHover();

  return (
    <Wrapper>
      <Styles className="row">
        <Div className="col-xs-4">
          {data.map(item => (
            <div className="row imageSpace">
              {hovered && <h1>{item.fields.name}</h1>}
              <img
                ref={ref}
                className="image"
                key={item.sys.id}
                alt="fall"
                src={item.fields.image.file.url}
              />
            </div>
          ))}
        </Div>

2条回答
ら.Afraid
2楼-- · 2020-04-19 19:34

I'd handle this by using CSS if at all possible, rather than handling hovering in my JavaScript code.

If doing it in JavaScript code, I'd handle this by creating a component for the things that are hovered:

function MyImage({src, header}) {
    const [ref, hovered] = useHover();
    return (
        <div className="row imageSpace">
          {hovered && <h1>{header}</h1>}
          <img
            ref={ref}
            className="image"
            alt="fall"
            src={src}
          />
        </div>
    );
}

and then use that component:

return (
  <Wrapper>
    <Styles className="row">
      <Div className="col-xs-4">
        {data.map(item =>
          <MyImage
            key={item.sys.id}
            src={item.fields.image.file.url}
            header={item.fields.name}
          />
        )}
      </Div>

(Obviously, make more of the props configurable if you like.)

查看更多
smile是对你的礼貌
3楼-- · 2020-04-19 19:47

As a general rule when you have a parent item with Array.map(), and functionality for each array item, refactor the items to a separate component (ImageRow in my code).

In this case you don't need to use refs for event handling, since React can handle that for you. Instead of return a ref from useHover, return an object with event handlers, and spread it on the component.

const { useState, useMemo } = React;

const useHover = () => {
  const [hovered, setHovered] = useState(false);
  
  const eventHandlers = useMemo(() => ({
    onMouseEnter: () => setHovered(true),
    onMouseLeave: () => setHovered(false)
  }), [setHovered]);
  
  return [hovered, eventHandlers];
};

const ImageRow = ({ name, url }) => {
  const [hovered, eventHandlers] = useHover();

  return (
    <div className="row imageSpace">
      {hovered && <h1>{name}</h1>}
      <img
        className="image"
        alt="fall"
        src={url}
        {...eventHandlers}
      />
    </div>
  );
};

const images = [{ id: 1, name: 'random1', url: 'https://picsum.photos/200?1' }, { id: 2, name: 'random2', url: 'https://picsum.photos/200?2' }, { id: 3, name: 'random3', url: 'https://picsum.photos/200?3' }];

const Wrapper = ({ images }) => (
  <div style={{ display: 'flex' }}>
  {images.map(({ id, ...props }) => <ImageRow key={id} {...props} />)}
  </div>
);

ReactDOM.render(
  <Wrapper images={images} />,
  root
);
h1 {
  position: absolute;
  pointer-events: none;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

查看更多
登录 后发表回答