0

Example is a functional component. I am using useRef() hook to get the ref a div element. I want to attach an event listener to the ref when Example mounts.

const Example: React.FC = () => {
  const ref = useRef<HTMLDivElement>(null);

  const handleClick = (): void => {
    // mouse click logic
  };

  useEffect(() => {
    if (ref && ref.current) {
      ref.current.addEventListener('click', handleClick);
    }

    return (): void => {
      if (ref && ref.current) {
        ref.current.removeEventListener('click', handleClick);
      }
    };
  }, []);

  return (
    <div ref={ref} />
  );
};

What's happening is that the ref is still null when Example did mount. So, I am unable to attach event listener to it. An alternative solution would be to assign an id to div and attach event listener to the DOM node directly which I don't really want to do.

Any guidance on how can I attach event listener to the ref will be really helpful :)

norbitrial
  • 14,716
  • 7
  • 32
  • 59
Vinay Sharma
  • 3,291
  • 4
  • 30
  • 66
  • 2
    What's the reason for using a ref to add a click listener? Does an `onClick` prop not suffice? – Nicholas Tower Apr 25 '20 at 14:16
  • The way you have used is correct. Adding a working snippet: https://stackblitz.com/edit/react-kkqwwd Can you create a demo for the issue you are getting? – Jagrati Apr 25 '20 at 14:19
  • @NicholasTower because it's not a good practice to pass onClick prop to non-interactive elements. – Vinay Sharma Apr 25 '20 at 14:21
  • `because it's not a good practice to pass onClick prop to non-interactive elements.` Say's who? Refs are an escape hatch for the things that react can't do declaratively. But react *can* do click listeners declaratively, so you should do so. – Nicholas Tower Apr 25 '20 at 14:21
  • @NicholasTower you can check here: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md – Vinay Sharma Apr 25 '20 at 14:23
  • The fix for that is to use a more descriptive tag than a `
    ` or to add a `role` attribute. By using a ref, the only thing you're accomplishing is making the code so hard to follow that the linter can't tell that you're making a mistake. But you're still making the same mistake: the element is still interactive, and still lacks a role.
    – Nicholas Tower Apr 25 '20 at 14:26
  • Alright, thank you everyone for such a wonderful guidance..! – Vinay Sharma Apr 25 '20 at 14:48
  • Hi guys, can someone answer this: https://stackoverflow.com/questions/61428958/react-js-how-to-animate-conditionally-rendered-components ? – Vinay Sharma Apr 25 '20 at 16:58

2 Answers2

0

You can use onClick to set the event.

const Example = () => {
  const handleClick = useCallback(() => {
    // mouse click logic
  }, [])

  return (
    <div onClick={handleClick}>Hello World</div>
  );
};

But if you would wanted to use the ref you need to understand fine some concepts. Firstly I have tested your code and the ref is ready when the useEffect is executed because the useEffect is executed after the DOM is drawn.

Secondly you need to understand the hook's dependencies because in your code the handleClick is created in each render but the useEffect is using the first reference created of this function so you can have problems if you use dependencies inside handleClick

Finally, I recommend you to invest useCallback, useState, useRef, useEffect and useReducer to know the differences and the best situations for each one.

93sauu
  • 3,770
  • 3
  • 27
  • 43
0

You do not need a ref for this. React supports click events out of the box using the standard declarative syntax.

const Example: React.FC = () => {
  const ref = useRef<HTMLDivElement>(null);

  const handleClick = (): void => {
    // mouse click logic
  };

  return (
    <div onClick={handleClick} />
  );
};

You mentioned in the comments that the reason you tried doing this was to silence a lint rule. But using a ref doesn't fix the problem that the linter is pointing out, it just makes the code complicated enough that the linter can't figure out what you're doing, and so doesn't realize that your code has the same problem as before.

To fix the lint issue, add a role attribute to the div so that screenreaders know what the purpose of this div is. Exactly which role to use depends on what the click handler is doing, but the most likely roles are probably "button" or "link".

return (
  <div onClick={handleClick} role="button" />
);
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • Thanks for sharing that information. Can you help me with as well: https://stackoverflow.com/questions/61428958/react-js-how-to-animate-conditionally-rendered-components ? – Vinay Sharma Apr 25 '20 at 16:59