'material-ui v4.0.1 warning "Expected an element that can hold a ref"
I upgraded to material-ui v4.0.1 and I see that Modals now require a forwarded ref. I'm having some trouble implementing a fix for this using class components and Dialogs.
I tried using React.createRef()
as well as React.forwardRef((props, ref) => (...)
in a few places but I can't figure out how to resolve this warning.
In my parent component I render a custom component
<ApolloFormDialog />
In ApolloFormDialog
I render essentially:
<Dialog ...>
{title}
{subtitle}
{form}
</Dialog>
The full warning is Warning: Failed prop type: Invalid prop 'children' supplied to 'Modal'. Expected an element that can hold a ref. Did you accidentally use a plain function component for an element instead?
However I am using class components currently. Migrating to use function components is not an option right now as my app is rather large.
I have tried adding a ref to ApolloFormDialog
as
<ApolloFormDialog ref={React.createRef()} />
as well as wrapping ApolloFormDialog
's class with:
export default React.forwardRef((props, ref) => <ApolloFormDialog ref={ref} {...props}/>)
and then adding that ref
to the dialog as
<Dialog ref={this.props.ref} />
but the warning still persists, and I'm not sure where to go from here.
Solution 1:[1]
My issue didn't actually have to do with Dialog
, but with the prop TransitionComponent
on Dialog
.
I switch between two types of transitions in my ApolloFormDialog
depending on if the screen is below a certain breakpoint, which was being called as function components:
<Dialog
open={open}
onClose={onRequestClose}
classes={{
paper: classnames(classes.dialogWidth, classes.overflowVisible),
}}
fullScreen={fullScreen}
TransitionComponent={
fullScreen ? FullscreenTransition : DefaultTransition
}
>
{content}
</Dialog>
FullscreenTransition
and DefaultTransition
come from a file and are defined as follows:
import React from 'react'
import Fade from '@material-ui/core/Fade'
import Slide from '@material-ui/core/Slide'
export function DefaultTransition(props) {
return <Fade {...props} />
}
export function FullscreenTransition(props) {
return <Slide direction='left' {...props} />
}
export function FullscreenExpansion(props) {
return <Slide direction='right' {...props} />
}
Changing these functions to the following fixed my issue:
import React from 'react'
import Fade from '@material-ui/core/Fade'
import Slide from '@material-ui/core/Slide'
export const DefaultTransition = React.forwardRef((props, ref) => (
<Fade {...props} ref={ref} />
))
export const FullscreenTransition = React.forwardRef((props, ref) => (
<Slide direction='left' {...props} ref={ref} />
))
export const FullscreenExpansion = React.forwardRef((props, ref) => (
<Slide direction='right' {...props} ref={ref} />
))
This was a relatively hard issue to solve on my end, so I'm going to leave this question up just in case someone else runs into a similar issue somewhere down the road.
Solution 2:[2]
I have had the same problem with "@material-ui/core/Tooltip" wrapping a new functional component. Even if the component was wrapped in a div inside its own code.
<!-- "Did you accidentally use a plain function component for an element instead?" -->
<Tooltip>
<NewFunctionalComponent />
</Tooltip>
<!-- Wrapped in a new div, devtools won't complain anymore -->
<Tooltip>
<div>
<NewFunctionalComponent />
</div>
</Tooltip>
<!-- No more warnings! -->
Solution 3:[3]
Wrapping my component to another div inside material-ui transition componet (like Slide, Fade etc) solves my issue. Example code:
From
<Slide direction='Right' in = {isSideNavOpen} mountOnEnter unmountOnExit>
<MyComponent />
</Slide>
To
<Slide direction='Right' in = {isSideNavOpen} mountOnEnter unmountOnExit>
<div><MyComponent /></div>
</Slide>`
Solution 4:[4]
React.forwardRef
fixes the issue for me, too.
To ensure that the React.forwardRef
solution works with Typescript, you must implement the following as per the documentation in Material-UI:
const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) {
return <Slide ref={ref} {...props} />;
});
See the source code in their demo here.
Solution 5:[5]
I was getting the same error
expected an element that cand hold a ref
I solved by adding a <Box></Box>
surrounding my children component.
From
<Modal open={open} onClose={handleClose}>
<DetailsCard />
</Modal>
To
<Modal open={open} onClose={handleClose}>
<Box>
<DetailsCard />
</Box>
</Modal>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Lief |
Solution 2 | |
Solution 3 | Arghya Sadhu |
Solution 4 | PacificFrost |
Solution 5 | Eduardo Pedra de Oliveira |