What is the correct proptype for a ref in React?

Reactjs

Reactjs Problem Overview


I am storing a ref in my redux store and using mapStateToProps to expose the ref for components that need access to it.

The ref that is stored looks like:

ref={node => this.myRefToBePutInReduxGlobalStore = node}

What is the correct propType for this ref?

Reactjs Solutions


Solution 1 - Reactjs

TL;DR

If you want to prop type a ref that only expects native DOM elements, such as a div or an input, the correct definition is the following:

refProp: PropTypes.oneOfType([
    // Either a function
    PropTypes.func, 
    // Or the instance of a DOM native element (see the note about SSR)
    PropTypes.shape({ current: PropTypes.instanceOf(Element) })
])

Answer to the specific issue described in the original post

In the OP question's example, it is not the ref prop type that needs to be declared, but the stuff pointed by the ref, and that will be passed from redux using mapStateToProps. Prop type for a DOM element would be: myRefToBePutInReduxGlobalStore: PropTypes.instanceOf(Element) (if it's a DOM element). Although, I would:

  1. Rename it to myElementToBePutInReduxGlobalStore (an element is not a ref)
  2. Avoid storing non-serializable data inside redux store
  3. Avoid passing elements in props as explained by React engineer Seb Markbage

Long answer to the actual question

Q: What is the correct proptype for a ref in React?

Example use case:

function FancyInput({ inputRef }) {
    return (
        <div className="fancy">
            Fancy input
            <input ref={inputRef} />
        </div>
    )
}

FancyInput.propTypes = {
    inputRef: ???   // What is the correct prop type here?
}

function App(props) {
    const inputRef = React.useRef()
    useLayoutEffect(function focusWhenStuffChange() {
        inputRef.current?.focus()
    }, [props.stuff])
    return <FancyInput inputRef={inputRef} />
}

Today, two kind of refs exist in react:

  1. An object which looks like this: { current: [something] }, usually created by React.createRef() helper or React.useRef() hook
  2. A callback function which will receive [something] as its argument (like the one in OP example)

Note: historically, you could also use a string ref, but it's considered legacy and will be removed from react

The second item is quite simple and requires the following prop type: PropTypes.func.

The first option is less obvious because you might want to specify the type of element pointed by the ref.

Many good answers

ref can point to something else than DOM element.

If you want to be totally flexible:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.any })
])

The above just enforces the shape of object ref w/ current property. It will work at all time for any kind of ref. For the purpose of using prop type, which is a way to spot any inconsistencies when you develop, this is probably enough. It seems very unlikely that an object with shape { current: [any] }, and is passed to a refToForward prop, would not be an actual ref.

However, you might want to declare that your component does not expect any kind of ref, but only a certain type, given what it needs that ref for.

I have setup a sandbox showcasing a few different way to declare a ref, even some non conventional, and then testing them with many prop types. You can find it here.


If you only expect a ref pointing to a native input element, not any HTML Element:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(HTMLInputElement) })
])

If you only expect a ref pointing to a React class component:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.instanceOf(Component) })
])

If you only expect a ref pointing to a functional component using useImperativeHandle to expose some public method:

refProp: PropTypes.oneOfType([
    PropTypes.func, 
    PropTypes.shape({ current: PropTypes.object })
])

Note: the above prop type is interesting because it also covers react components and native DOM elements, because they all are inheriting the base javascript Object


There is no single good way to declare the prop type for a ref, it depends of the usage. If your ref points to something very identified, it can be worth adding a specific prop type. Otherwise, just checking the general shape seems enough.


Note about server side rendering

If your code has to run on the server, unless you already polyfill DOM environment, Element or any other client-side type will be undefined in NodeJS. You can use the following shim to support it:

Element = typeof Element === 'undefined' ? function(){} : Element

The following React Docs pages gives more insight on how to use refs, both object and callbacks, and also how to use useRef hook

Answer updated thank to @Rahul Sagore, @Ferenk Kamra, @svnm, and @Kamuela Franco

Solution 2 - Reactjs

Very similar to @Pandaiolo's post,

PropTypes.elementType has now been added

forwardedRef: PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({ current: PropTypes.elementType })
]),

If using PropTypes >= 15.7.0 PropTypes.elementType was added February 10, 2019 in this PR

Solution 3 - Reactjs

I only check for the preferred way and since PropType.object is not very valuable I ended up using this:

PropTypes.shape({ current: PropTypes.instanceOf(Element) })

Solution 4 - Reactjs

I checked all the above answers but I get a warning from the react, so I researched a lot and I got the right answer.

import React, { Component } from 'react';

ComponentName.propTypes = {
  reference: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({ current: PropTypes.instanceOf(Component) }),
  ]),
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondanday74View Question on Stackoverflow
Solution 1 - ReactjsPandaioloView Answer on Stackoverflow
Solution 2 - ReactjssvnmView Answer on Stackoverflow
Solution 3 - ReactjsFerenc KamrasView Answer on Stackoverflow
Solution 4 - ReactjsNisharg ShahView Answer on Stackoverflow