Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct proptype for a ref in React?

Tags:

reactjs

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?

like image 259
danday74 Avatar asked Dec 28 '17 12:12

danday74


People also ask

How do you set a ref in React?

You can create a ref by calling React. createRef() and attaching a React element to it using the ref attribute on the element. We can “refer” to the node of the ref created in the render method with access to the current attribute of the ref.

What is REF IN React example?

Refs are a function provided by React to access the DOM element and the React element that you might have created on your own. They are used in cases where we want to change the value of a child component, without making use of props and all.

How ref works in React?

Refs is the shorthand used for references in React. It is similar to keys in React. It is an attribute which makes it possible to store a reference to particular DOM nodes or React elements. It provides a way to access React DOM nodes or React elements and how to interact with it.

How do you define an object Proptype in React?

PropTypes is React's internal mechanism for adding type checking to components. React components use a special property named propTypes to set up type checking. When props are passed to a React component, they are checked against the type definitions configured in the propTypes property.


2 Answers

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

like image 171
Pandaiolo Avatar answered Oct 22 '22 01:10

Pandaiolo


Very similar to @Pandaiolo's post,

PropTypes.elementType has now been added

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

PropTypes.instanceOf(Element) looks to be the correct way to do this now as mentioned by Ferenc

like image 22
svnm Avatar answered Oct 22 '22 01:10

svnm