react: Unexpected warning when hydrating with portal and SSR
Do you want to request a feature or report a bug?
bug
What is the current behavior?
Given the following (simplified) snippet:
class HoverMenu extends React.Component {
render() {
if (typeof document === 'undefined') return null
const root = document.getElementById('root')
return ReactDOM.createPortal(<div>Hello World</div>, root)
}
}
class Para extends React.Component {
render() {
return (
<span>
Some Text
<HoverMenu />
</span>
)
}
}
where div#root
is a valid div
that exists, the following error is shown when hydrating after SSR:
Warning: Expected server HTML to contain a matching <div> in <span>
The warning goes away if I update the definition of HoverMenu
to:
class HoverMenu extends React.Component {
componentDidMount() {
this.setState({ isActive: true })
}
render() {
const { isActive} = this.state
if (!isActive) return null
const root = document.getElementById('root')
return ReactDOM.createPortal(<div>Hello World</div>, root)
}
}
I’d prefer not to do that because of the double rendering caused by setState
in componentDidMount
.
I don’t quite understand what that error is telling me. No <div />
is rendered server-side in either case. The error is particularly confusing, as the HoverMenu
DOM div
is not even rendered inside a DOM span
. (I wonder if this is happening because HoverMenu
is nested inside a React span
.)
What is the expected behavior?
No error is thrown. Or, at least that the error message is clearer.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Chrome 65 React 16.2 (SSR through Next 5.1)
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 14
- Comments: 28 (3 by maintainers)
While hydrating portals is not supported (https://github.com/facebook/react/issues/13097), the message itself doesn’t make sense. We’ll need to investigate and fix it.
That’s one use case but there are also others like sidebars and similar which are not necessarily client-only.
Right — in the current design. It could change. https://github.com/facebook/react/pull/8386#issuecomment-262375265
The value of throwing is to explicitly acknowledge that portal won’t work. You can easily work around it with
{domNode && ReactDOM.createPortal(stuff, domNode)}
or similar. Because you already had to do some kind of checking to determine whether you can get the DOM node — so at this point you should have enough data to choose not to emit the portal.It doesn’t seem like rendering nothing is best — I don’t see what makes portal contents different that we don’t consider it worth server rendering.
We’ll likely come back to this together with the revamp of server renderer for suspense.
Hello, is anyone working on this issue? If not I would like to take it.