react-portal-universal
react-portal-universal copied to clipboard
Support concurrent rendering with SC-inspired API
Fixes #5, and I think #2.
Heavily inspired by styled-components
, this PR completely changes the API, but should make things concurrent-safe:
// CLIENT
import { UniversalPortal, prepareClientPortals } from "react-portal-universal";
const Head = ({ children }) => (
// pass selector for a document.querySelector
// instead of a DOM node like in createPortal
<UniversalPortal selector="head">{children}</UniversalPortal>
);
class App extends React.Component {
render() {
return (
<article>
<Head>
<title>Hello, World!</title>
<meta name="description" content="Lorem ipsum..." />
</Head>
<h1>Hello, World!</h1>
<p>
Lorem ipsum sit doloret um.
</p>
</article>
);
}
}
// remove static markup and allow React
// to render only actual components
prepareClientPortals();
ReactDOM.render(<App />, document.querySelector("#root"));
// SERVER
const { ServerPortal } = require("react-portal-universal/server");
const portals = new ServerPortal();
const element = portals.collectPortals(<App />);
const body = ReactDOMServer.renderToString(element));
const template = fs.readFileSync(path.resolve("build/index.html"), "utf8");
const html = template.replace("<div id=\"root\"></div>", `<div id="root">${body}</div>`);
const markup = portals.appendUniversalPortals(html);
res.status(200).send(markup);
Notes:
- I've published this as
@jesstelford/react-portal-universal
for testing out - I have no idea how to update the tests, so I haven't touched them.
- Feedback is appreciated! <3
Here's an example of using it with next.js: https://github.com/zeit/next.js/tree/canary/examples/with-portals-ssr
Do you either of you need assistance in getting this pushed through?
EDIT: oh duh, the below is exactly what you do 🤦♂
Why not use context to pass the per-render portal state around?
Wrap the app with the Context.Provider and that removes the new ServerPortal()
call.
export const PortalContext = React.createContext()
const UniversalPortalProvider = ({children, store}) => (
<PortalContext.Provider value={store}>
{children}
</PortalContext.Provider>
)
if there's a store, it means it got passed by the SSR, and the portals use it, otherwise they're on the client.
Will this PR be merged? Can I help?
@svobik7 Thank you for your interest. I don’t actively maintain this so if you want a quick fix feel free to fork it and in the meantime I try to find some time to look into that. Sine I neglected this project I’ll need a moment or two to get back on track
If you just make sure this actually solves your problem that’s great