What is the correct proptype for a ref in React?
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
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:
- Rename it to
myElementToBePutInReduxGlobalStore
(an element is not a ref) - Avoid storing non-serializable data inside redux store
- 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:
- An object which looks like this:
{ current: [something] }
, usually created byReact.createRef()
helper orReact.useRef()
hook - 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