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

[@types/react] useRef return type clashes with ref prop type #35572

Closed
3 tasks done
mateja176 opened this issue May 19, 2019 · 19 comments
Closed
3 tasks done

[@types/react] useRef return type clashes with ref prop type #35572

mateja176 opened this issue May 19, 2019 · 19 comments

Comments

@mateja176
Copy link
Contributor

mateja176 commented May 19, 2019

Type 'MutableRefObject<HTMLDivElement | undefined>' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
  Type 'MutableRefObject<HTMLDivElement | undefined>' is not assignable to type 'RefObject<HTMLDivElement>'.
    Types of property 'current' are incompatible.
      Type 'HTMLDivElement | undefined' is not assignable to type 'HTMLDivElement | null'.
        Type 'undefined' is not assignable to type 'HTMLDivElement | null'.
@mateja176 mateja176 changed the title [@types/react] Type useRef [@types/react] useRef return type clashes with ref prop type May 19, 2019
@AdrianMrn
Copy link
Contributor

AdrianMrn commented May 20, 2019

EDIT 2: Disregard my solution, use the one below :)

I currently have the same problem, have you found a solution already by any chance?

EDIT: I'm now using this, admittedly not so pretty, solution:

const inputField = React.useRef() as React.MutableRefObject<HTMLInputElement>;

Found here: #28884 (comment)

@shane935
Copy link
Contributor

shane935 commented Jun 3, 2019

A slightly neater solution is to set the initialValue to null in the useRef function call

const componentRef = useRef<HTMLDivElement>(null);

this ensures that componentRef = HTMLDivElement | null which is what the ref prop is expecting rather than HTMLDivElement | undefined.

@wcandillon
Copy link
Contributor

@shane935 you have made my day sir 🙌🏻

@staeke
Copy link
Contributor

staeke commented Jul 29, 2019

Seems to me undefined should be added to RefObject<T>.current like:

//index.ts, line 80
interface RefObject<T> {
   readonly current: T | null;
}

so it reads

//index.ts, line 80
interface RefObject<T> {
   readonly current: T | null | undefined;
}

@Jessidhia
Copy link
Member

It's not a bug, it's catching a legitimate typing error.

For more details, see #38228 (comment)

@osdatu
Copy link

osdatu commented Apr 15, 2020

Just as @Jessidhia say, it's not a bug , maybe you should write like this.

import React, {
  forwardRef,
  ForwardRefRenderFunction,
  useImperativeHandle,
  useRef,
  PropsWithChildren,
} from "react";

interface Props {}
interface Ref {
  value: string;
  setValue: (value: string) => void;
}

const InputEmail: ForwardRefRenderFunction<Ref, PropsWithChildren<Props>> = (
  props,
  ref
) => {
  // hooks
  const inputElement = useRef<HTMLInputElement | null>(null);
  useImperativeHandle(ref, () => {
    return {
      value: inputElement.current ? inputElement.current.value : "",
      setValue: (value: string) => {
        inputElement.current && (inputElement.current.value = value);
      },
    };
  });

  // render
  return (
    <div>
      <input
        type="text"
        ref={inputElement}
        defaultValue="admin@demo.com"
        disabled
      />
    </div>
  );
};

const Component = forwardRef(InputEmail);

export default Component;

@konfika
Copy link

konfika commented May 20, 2020

I'm not sure this will fix everyone's problem, and I'm no expert on this stuff, but after looking at the typings of what I was passing in as a ref and the actual ref type of the component I was passing the ref into, it seemed as though I had the interfaces wrong.

I initially had an interface of props like so for the receiving component:

type TextboxProps = CustomProps & React.HTMLProps<HTMLInputElement>

which I changed to:

type TextboxProps = CustomProps & React.HTMLAttributes<HTMLInputElement>

the typing error then disappeared.

I'm not going to pretend I know more than I do, but looking into the typings for the HTMLProps interface and the React.forwardRef() function type you can see that it is using RefAttributes. Just above that, you can see the ClassAttributes type that HTMLProps is extending, of which defines the LegacyRef type which I believe was causing my typing issue.

@ProdigySim
Copy link
Contributor

It looks like this project has 3 overloads for useRef(), do we need all 3? It seems like the useRef<T>(null) case is the one developers will use most of the time.

    function useRef<T>(initialValue: T): MutableRefObject<T>;
    function useRef<T>(initialValue: T|null): RefObject<T>;
    function useRef<T = undefined>(): MutableRefObject<T | undefined>;
  1. What is the expect use case of the two mutable overloads?
  2. Is there some other way this can be supported to that users can more easily find the "correct" overload?

@joshribakoff
Copy link

joshribakoff commented Aug 23, 2020

I have RefObject:

Screen Shot 2020-08-23 at 1 06 16 PM

I pass it to my Panel component as the prop ref:

Screen Shot 2020-08-23 at 1 06 04 PM

I try to consume w/ forwardRef, it becomes MutableRefObject:

Screen Shot 2020-08-23 at 1 06 56 PM

I'm now unable to consume the .current property:

Screen Shot 2020-08-23 at 1 10 27 PM

@Jessidhia @osf2e your suggestion does not seem applicable here.

The workaround I've had to resort to:

        if (ref && (ref as RefObject<HTMLDivElement>).current) {
              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
              (ref as RefObject<
                HTMLDivElement
              >)!.current!.style.height = `${containerHeight}px`;
            }

@Seanmclem
Copy link

Using react-native-web, I had to use.

import {RefObject} from 'react';
// ...
const myRef = useRef() as RefObject<View>

@lior-chervinsky
Copy link

lior-chervinsky commented Oct 21, 2020

I had this problem with a Ref for an Input element
So i switched from useRef<HTMLDivElement>() -> useRef<HTMLInputElement>() and it worked OK

@Tobbe
Copy link

Tobbe commented Nov 3, 2020

@joshribakoff You can also type the parameters directly

export const Panel = React.forwardRef((props: PanelProps, ref: React.RefObject<HTMLDivElement>) => {

That way you shouldn't have to typecast when accessing your ref.
But I still don't understand why we have to do this...

@PRFTCanoaf
Copy link

I used this when passing a ref to a form:

const formRef = useRef<HTMLFormElement | null>(null);

return <form ref={formRef}></form>

@orta
Copy link
Collaborator

orta commented Jun 7, 2021

Hi thread, we're moving DefinitelyTyped to use GitHub Discussions for conversations the @types modules in DefinitelyTyped.

To help with the transition, we're closing all issues which haven't had activity in the last 6 months, which includes this issue. If you think closing this issue is a mistake, please pop into the TypeScript Community Discord and mention the issue in the definitely-typed channel.

@kenny1983
Copy link

A slightly neater solution is to set the initialValue to null in the useRef function call

const componentRef = useRef<HTMLDivElement>(null);

this ensures that componentRef = HTMLDivElement | null which is what the ref prop is expecting rather than HTMLDivElement | undefined.

I feel like I'm losing my @#$%ing mind here, coz @shane935 received a lot of love for his comment, but it produces the exact same error for me!

Honestly, I feel like the entire move to TypeScript from vanilla JS has been a constant battle against the over-complicated strictness of the compiler.

Now I'm stuck between a rock and a hard place: do I go back to JS and relish in the fact that simple statements like this just work, or do I continue on this epic journey of trying to produce "safer" code?

If I was working on a project for my boss, the answer would be obvious: @#$% off TS and go back to actually getting work done, instead of wasting (potentially) weeks on this constant fight with the TS compiler. But I'm working on my first TS project "just for fun", and although I'm not having much fun ATM, I feel as though this shouldn't be so hard and that the benefits should outweight the effort required. IDK though...what do you guys think??

@onpaws
Copy link

onpaws commented Sep 10, 2021

I know what you mean @kenny1983 had similar feelings when I was starting out in TS also :)
Have worked with people who hate it and others who swear by and love it.

One 'pro' argument to static checking I've seen, but can be hard to appreciate as a developer first starting out with TS and feeling suffocated by the type checker, is that when your code is typed properly, TS static checks should in principle forestall a whole category of exceptions your end users might otherwise encounter without your awareness. Reason you might never know about it is, unlike on the server side, JS errors in the browser don't get sent to a server by default (not without you setting up some kind of analytics/error reporting etc).

As you know with SPAs, including React, unhandled exceptions can be quite devastating b/c the whole app may unexpectedly just "turn white"/crash, depending. TS should help avoid at least some of the reasons for this.

@kenny1983
Copy link

kenny1983 commented Sep 10, 2021

Thanks @onpaws for both the empathy and the example of how TS is worth the effort 👍.

After writing the wild rant above (LOL lets face it, that's what it was 😛) I ended up spending the rest of my night and well into the early hours of the morning reading blogs re: why people love or hate it, then listening to a podcast from Scott Hanselman where he talks to Anders Hejlsberg about his intentions behind creating the language. I thoroughly recommend a listen!

Another great resource is this keynote presentation from the GOTO Conference 2012 where Anders demonstrates the power of static typing-powered tools. I already knew this having come from a C# background, but it never actually dawned on me why C# IntelliSense and the equivalent IDE features for JS are so far apart in what they can achieve. Very cool! 😎

So I think I'll keep using TS as Anders originally intended: a smart tooling enabler and nothing more. After all, compiled TS is just JS without the type annotations anyway, right?? And if/when I find myself wrestling the type checker again, I'll just throw a // @ ts-ignore at it and move on with my life!

@hazratgs
Copy link
Contributor

hazratgs commented Apr 6, 2022

There is another way to solve the problem:

export const Input = forwardRef<HTMLTextAreaElement, IInputProps>((props, ref) => {
  const inputRef = useRef<HTMLTextAreaElement | null>()

  const createRef = (node: HTMLTextAreaElement | null) => {
    inputRef.current = node
    if (typeof ref === 'function') {
      ref(node)
    } else if (ref) {
      (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node
    }
  }

  const clickUploadFileHandler = useCallback(() => {
    if (inputRef) {
      inputRef.current?.focus()
    }
  }, [])

  return (
    <>
      <s.Input ref={createRef} />
      <FileUpload files={files} onClick={clickUploadFileHandler} />
    </>
  )
})

gidjin added a commit to USSF-ORBIT/ussf-portal-client that referenced this issue Aug 11, 2022
abbyoung pushed a commit to USSF-ORBIT/ussf-portal-client that referenced this issue Aug 11, 2022
jcbcapps added a commit to USSF-ORBIT/ussf-portal-client that referenced this issue Aug 23, 2022
* Change current news path to news-announcments

* Update test

* Add NewsCarousel and NewsCarouselItem components

* Update storybook component

* Add CTA

* Add query for carousel articles

* Add NewsCarousel component to page

* Add custom ellipses and custom arrows

* Update styles

* Move class to module

* fix: resolve type error from useRef return

See for where the solution came from DefinitelyTyped/DefinitelyTyped#35572 (comment)

* test: add article mock to NewsAnnouncements component

* fix a11y violation in carousel, update mock in test

* Style updates

* Add test

* Add test

* Add news page from main

* Add view older internal news link

* Add comment

* Update styles

* Add conditional render for link

* Remove unnecessary importants

* Update default image

* Update tests

* Remove flex styles

* Add comment

Co-authored-by: John Gedeon <john@truss.works>
Co-authored-by: Abigail Young <abbyoung@gmail.com>
jcbcapps added a commit to USSF-ORBIT/ussf-portal-client that referenced this issue Aug 23, 2022
)

* Change current news path to news-announcments

* Update test

* Add NewsCarousel and NewsCarouselItem components

* Update storybook component

* Add CTA

* Add query for carousel articles

* Add NewsCarousel component to page

* Add custom ellipses and custom arrows

* Update styles

* Move class to module

* fix: resolve type error from useRef return

See for where the solution came from DefinitelyTyped/DefinitelyTyped#35572 (comment)

* test: add article mock to NewsAnnouncements component

* fix a11y violation in carousel, update mock in test

* Style updates

* Add test

* Add test

* Add news page from main

* Add view older internal news link

* Add comment

* Update styles

* Add conditional render for link

* Update text

* Display four latest articles

* Update link

* Update button text

* Update test

* Update test

* Update page copy

* Update RSS query

* Update test

* Update test

* Update classname

Co-authored-by: John Gedeon <john@truss.works>
Co-authored-by: Abigail Young <abbyoung@gmail.com>
gidjin added a commit to USSF-ORBIT/ussf-portal-client that referenced this issue Aug 24, 2022
## [4.7.0](4.6.0...4.7.0) (2022-08-24)


### Features

* Add internal news carousel for news and announcements page ([#744](#744)) ([c4acfe6](c4acfe6)), closes [/github.com/DefinitelyTyped/DefinitelyTyped/issues/35572#issuecomment-498242139](https://github.com/USSF-ORBIT//github.com/DefinitelyTyped/DefinitelyTyped/issues/35572/issues/issuecomment-498242139)
* Display Spaceforce.mil news articles below the news carousel ([#746](#746)) ([e13b57a](e13b57a)), closes [/github.com/DefinitelyTyped/DefinitelyTyped/issues/35572#issuecomment-498242139](https://github.com/USSF-ORBIT//github.com/DefinitelyTyped/DefinitelyTyped/issues/35572/issues/issuecomment-498242139)
@theronburger
Copy link

Had the same issue.

I was using both TypeScript and ESLint with the react/require-default-props rule enabled.
This rule enforces that every prop that is not marked as required should have a corresponding defaultProps value. However, with TypeScript, you don't need defaultProps, as optional props and default values can be handled directly in the function signature.

To resolve the issue, you can disable the react/require-default-props rule in your ESLint configuration file (.eslintrc or similar). In your ESLint configuration file, add or update the rules section like this:

{
  "rules": {
    "react/require-default-props": "off"
  }
}

This will disable the react/require-default-props rule, and you shouldn't see the error anymore. Since you are using TypeScript to handle optional props and default values, this rule is not necessary for your project.

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