Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hooks with Typescript: ts(2339) Property 'handleClickOutside' does not exist. #310

Open
aliankarali opened this issue Feb 8, 2019 · 9 comments

Comments

@aliankarali
Copy link

aliankarali commented Feb 8, 2019

Update: Another thing I noticed with this pattern is that when you have multiple instances of the same Component, only the last one triggers handleClickOutside.

Here is my code, grossly simplified (sorry if it is more of a TS question rather than this library, but in any way it would be nice to see an example).

error: Property 'handleClickOutside' does not exist on type '({ options, }: Props) => Element'. ts(2339)

The code works fine but in the editor Dropdown.handleClickOutside lines highlights this error.
Other than using // @ts-ignore, how can I solve this issue? how can I specify the property?

import * as React from 'react';
import onClickOutside from 'react-onclickoutside';

interface Props {
    options: string[];
}

export const Dropdown = ({
    options,
}: Props) => {
    const [isOpen, setIsOpen] = React.useState(false);

    Dropdown.handleClickOutside = () => {
        setIsOpen(false);
    };

    return (
        <div>
            <ul isOpen={isOpen}>
                {options.map((option, i) => {
                    return (
                        <li
                            key={i}
                        >
                            {option}
                        </li>
                    );
                })}
            </ul>
        </div>
    );
};

export default onClickOutside(
    Dropdown,
    { handleClickOutside: () => Dropdown.handleClickOutside }
);
@pasih
Copy link

pasih commented Mar 7, 2019

try this in the same file:

namespace Dropdown {
  export let handleClickOutside: () => void;
}

@aliankarali
Copy link
Author

thanks for the tip 👍

@nerdmax
Copy link

nerdmax commented Mar 21, 2019

I tried this solution but typescript complains this:

Cannot redeclare block-scoped variable 'Dropdown'.

@n10v
Copy link

n10v commented Apr 8, 2019

I also tried it and I get Duplicate identifier 'Dropdown'.
react-onclickoutside v6.8.0, typescript v.3.3.4000

@hlolli
Copy link

hlolli commented Jun 18, 2019

Third on the train, the create-react-app default linter doesn't like it

Failed to compile.

./src/components/MenuBar/MenuBar.tsx
  Line 45:  ES2015 module syntax is preferred over custom TypeScript modules and namespaces  @typescript-eslint/no-namespace

Search for the keywords to learn more about each error.

not an error, but if someone could suggest an alternative solution, would be great.

@n10v
Copy link

n10v commented Jun 19, 2019

As a solution you can get rid of this library and use this small hook from https://usehooks.com/useOnClickOutside/. It works for me really well:

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

// Usage
function App() {
  // Create a ref that we add to the element for which we want to detect outside clicks
  const ref = useRef();
  // State for our modal
  const [isModalOpen, setModalOpen] = useState(false);
  // Call hook passing in the ref and a function to call on outside click
  useOnClickOutside(ref, () => setModalOpen(false));

  return (
    <div>
      {isModalOpen ? (
        <div ref={ref}>
          👋 Hey, I'm a modal. Click anywhere outside of me to close.
        </div>
      ) : (
        <button onClick={() => setModalOpen(true)}>Open Modal</button>
      )}
    </div>
  );
}

// Hook
function useOnClickOutside(ref, handler) {
  useEffect(
    () => {
      const listener = event => {
        // Do nothing if clicking ref's element or descendent elements
        if (!ref.current || ref.current.contains(event.target)) {
          return;
        }

        handler(event);
      };

      document.addEventListener('mousedown', listener);
      document.addEventListener('touchstart', listener);

      return () => {
        document.removeEventListener('mousedown', listener);
        document.removeEventListener('touchstart', listener);
      };
    },
    // Add ref and handler to effect dependencies
    // It's worth noting that because passed in handler is a new ...
    // ... function on every render that will cause this effect ...
    // ... callback/cleanup to run every render. It's not a big deal ...
    // ... but to optimize you can wrap handler in useCallback before ...
    // ... passing it into this hook.
    [ref, handler]
  );
}

@aliankarali aliankarali reopened this Sep 30, 2019
@aliankarali
Copy link
Author

Thanks for the hooks @BoGeM ;) For those looking for an external solution I would recommend https://github.com/airbnb/react-outside-click-handler

@softatac
Copy link

softatac commented Apr 7, 2021

Defining the handler on the function after its definition worked for me. I think it's ok, as its value will be set on rendering the component anyways.

import {useState} from 'react'
import onClickOutside from 'react-onclickoutside'

const UserInfo = () => {
  const [menuVisible, setMenuVisible] = useState(false)
  UserInfo.clickOutside = () => setMenuVisible(false)

  return (
    <div className="cursor-pointer"
      onClick={() => setMenuVisible(!menuVisible)}      
    >
      <p>Click me</p>
      <div className={!menuVisible && 'hidden'}>
        <p>I am zee menu</p>
        <p>I am zee menu</p>
      </div>
    </div>
  )
}
UserInfo.clickOutside = null /// !! this

export default onClickOutside(UserInfo, {
  handleClickOutside: () => UserInfo.clickOutside,
})

@Pomax
Copy link
Owner

Pomax commented Apr 7, 2021

Just a note: if you've already written the code to make hooks work, PRs that add a hook solution are most certainly welcome, and I'll be more than happy to push out a new version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants