-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Does not work with SSR #217
Comments
Also don't love seeing |
I don't currently use this lib with SSR. PRs appreciated! |
Weird to see the OP having an error with SSR, it doesn't seem like a portal should render on the server at all due to the https://github.com/tajo/react-portal/blob/master/src/Portal.js#L15 It does make sense that we're seeing the mismatch server-to-client, however. I wonder if returning an empty |
Let's add a state variable and make it true on This will help to support SSR. |
I think I understand why I am seeing this. I server-side render my React app. However, I use several libraries that unsafely reference I need to keep this at the moment but I'm wondering if it'd be straightforward to expose an optional property on the component that will be used in place of Maybe a function property |
That makes sense; would be easy enough and is backward compatible. Could throw the rely-on-state fix that @vishalvijay mentioned in there, too. 💃 |
Do you have to stub-out even There are many other options than adding additional API to the
That would cause re-rendering. |
I might not need to though I'll need to check. Can you apply stub-outs only in places with those unsafe libs? I'm not sure what you mean here. So I have a
Do you mean fork it and implement a new I suppose the support should only be added to |
Component should manipulate the DOM only in Or does it touch the DOM once you My point: There is no good reason why you should stub out |
It was already suggested above, but this was my solution to this:
Here's some code: import React, { useState, useEffect } from "react";
import { Portal } from "react-portal";
const MyComponent = props => {
const [hasMounted, setHasMounted] = useState(false);
// Will be called on initial mount.
useEffect(() => {
setHasMounted(true);
}, []);
// Note:
// This check is necessary for this component to work when used with SSR.
// While react-portal will itself check if window is available, that is not
// enough to ensure that there arent discrepancies between what the server
// renders and what the client renders, as the client *will* have access to
// the window. Therefore, we should only render the root level portal element
// once the component has actually mounted, as determined by a state variable.
if (!hasMounted) {
return null;
}
return (
<Portal>
{
// ... etc ...
}
</Portal>
);
} Note that this wont always be required. For example, if your component is included in some other component based on some condition that is only possible given some user interaction (e.g.; a button click that sets some kind of However, in my case I am using |
For nextjs users, check their example here: https://github.com/zeit/next.js/tree/canary/examples/with-portals Here's the relevant piece: import React from 'react'
import ReactDOM from 'react-dom'
export class Portal extends React.Component {
componentDidMount () {
this.element = document.querySelector(this.props.selector)
this.forceUpdate()
}
render () {
if (this.element === undefined) {
return null
}
return ReactDOM.createPortal(this.props.children, this.element)
}
} Basically return null on ssr and render on client, like suggested above by many others. In case you do want to render it on server and have the time to experiment, check cheerio out, import * as ReactDOMServer from "react-dom/server";
import { load } from "cheerio";
import { flushUniversalPortals } from "./index";
export function appendUniversalPortals(html: string) {
const portals = flushUniversalPortals();
if (!portals.length) {
return html;
}
const $ = load(html);
portals.forEach(([children, selector]) => {
const markup = ReactDOMServer.renderToStaticMarkup(children);
$(markup).attr("data-react-universal-portal", "").appendTo((selector as any))
});
return $.html({ decodeEntities: false });
} Note that this is just a concept, their lib shouldn't be used in prod since portals will be shared between the requests, if you manage to implement this isolating the requests (which should be possible with next) then you're fine. |
When running this code with SSR I am seeing this:
Invariant Violation: Portals are not currently │ supported by the server renderer. Render them conditionally so that they only appear on the client render.
React 16.3
The text was updated successfully, but these errors were encountered: