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

[Basic] useRef in useEffect Object is possibly undefined #187

Closed
thisconnect opened this issue Feb 16, 2020 · 17 comments
Closed

[Basic] useRef in useEffect Object is possibly undefined #187

thisconnect opened this issue Feb 16, 2020 · 17 comments
Assignees
Labels
BASIC Basic Cheatsheet wontfix This will not be worked on

Comments

@thisconnect
Copy link

What cheatsheet is this about? (if applicable)

How to use typescript and useRef

What's your issue or idea?

import React, { useEffect, useRef } from 'react'
export default function() {
  const myRef = useRef()
  useEffect(() => {
    myRef.current.classList.add('something')
    // Object is possibly 'undefined'.ts(2532)
  })
  return (
    <div ref={myRef}></div>
  )
}

The following "works" / "typescript doesn't complain"... but is that the proper way?

import React, { useEffect, useRef } from 'react'

export default function() {
  const myRef = useRef(null)
  useEffect(() => {
    const node = myRef.current as any
    node.classList.add('something')
  })
  return (
    <div ref={myRef}></div>
  )
}
@thisconnect thisconnect added the BASIC Basic Cheatsheet label Feb 16, 2020
@thisconnect
Copy link
Author

Typescript seems to like this one

import React, { useEffect, useRef } from 'react'

export default function() {
  const myRef = useRef<HTMLDivElement>(null)
  useEffect(() => {
    const node = myRef.current
    node?.classList.add('something')
    // ^ note the new ? similar to `if (node)`
  })
  return (
    <div ref={myRef}></div>
  )
}

@ferdaber
Copy link
Collaborator

ferdaber commented Feb 16, 2020 via email

@thisconnect
Copy link
Author

That second one is the proper usage.

ok thanks I'll go with that, but technically useRef does not directly return a HTMLDivElement, is that correct?

It returns some object that has a current property that is the HTMLDivElement… Am I missing something?

@hellatan
Copy link

useRef is a generic so you can pass any type you want into it so that it can internally know what .current is supposed to be. Since you're using null as the initial value, I think you need to add that to your type you're declaring as well. So useRef<HTMLDivElement>(null) should be useRef<HTMLDivElement | null>(null) so typescript knows your myRef.current property can be HTMLDivElement or null.

You don't always have to declare the type your ref will be if you know the type won't change. For example, if you do something like

const myRef = useRef(false);
if (!myRef.current) {
    myRef.current = true
}

typescript will infer it as a boolean:

image

@ferdaber
Copy link
Collaborator

@hellatan there are overloads for useRef such that if you use a type param T vs T | null the resultant object is different:

// @type = { readonly current: HTMLDivElement | null }
const refObjectToPassIntoRefProp = useRef<HTMLDivElement>(null)
// @type = { current: HTMLDivElement | null }
const refObjectYouMaintain = useRef<HTMLDivElement | null>(null)
// @type = { current: HTMLDivElement | undefined }
const refObjectYouMaintainToo = useRef<HTMLDivElement>()

With the first, the current property of the object is read-only to you, the consumer. It's meant to be passed into React as the pointer is maintained by React. In practice, you can still mutate the current property of something you pass into React but it's not recommended as React is supposed to be the owner.

@hellatan
Copy link

@ferdaber so do you mean when you don't pass in T | null, typescript will complain if you try to do myRef.current = 'some new ref'?

@ferdaber
Copy link
Collaborator

Correct.

@thisconnect
Copy link
Author

Good to know, it makes sense now. I was looking for readonly (but didn't know it exists) so your first example useRef<HTMLDivElement>(null) is exactly what I want.

@thisconnect
Copy link
Author

The 3rd example errors for me

// @type = { current: HTMLDivElement | undefined }
const refObjectYouMaintainToo = useRef<HTMLDivElement>()

Error

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'.  TS2322

code snippet (top component no conditional render)

  const myRef = useRef<HTMLDivElement>()
  return (
    <div className="App">
      <div ref={myRef}></div>

Maybe because I use CRA, which is a little behind
@types/react current 6.9.19, latest 16.9.21

@thisconnect
Copy link
Author

updating typescript to 3.8.2 and @types/react to 16.9.21 didn't help

@ferdaber
Copy link
Collaborator

If you're using a ref object to pass into a ref prop for an element, you need to use the | null overload, otherwise it will complain to you. The third form is for cases where you maintain the pointer yourself (instead of React).

@thisconnect
Copy link
Author

thanks for your explanation

@swyxio
Copy link
Collaborator

swyxio commented Mar 25, 2020

@thisconnect you are welcome to PR if you feel the cheatsheet could be improved to help you on this :)

@ShubhamChaudharyy
Copy link

ShubhamChaudharyy commented Aug 23, 2020

why not use like this "useRef<any>()" it works fine btw

@swyxio swyxio reopened this Aug 23, 2020
@stale
Copy link

stale bot commented Oct 22, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions!

@stale stale bot added the wontfix This will not be worked on label Oct 22, 2020
@swyxio
Copy link
Collaborator

swyxio commented Oct 22, 2020

think we have addressed this well here https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/hooks/#useref

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BASIC Basic Cheatsheet wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

5 participants