react-portal-universal icon indicating copy to clipboard operation
react-portal-universal copied to clipboard

Support concurrent rendering with SC-inspired API

Open jesstelford opened this issue 6 years ago • 5 comments

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:

  1. I've published this as @jesstelford/react-portal-universal for testing out
  2. I have no idea how to update the tests, so I haven't touched them.
  3. Feedback is appreciated! <3

jesstelford avatar Feb 18 '19 03:02 jesstelford

Here's an example of using it with next.js: https://github.com/zeit/next.js/tree/canary/examples/with-portals-ssr

jesstelford avatar Feb 20 '19 04:02 jesstelford

Do you either of you need assistance in getting this pushed through?

davidpatrick avatar Aug 27 '19 18:08 davidpatrick

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.

wmertens avatar Sep 05 '19 10:09 wmertens

Will this PR be merged? Can I help?

svobik7 avatar Aug 05 '20 20:08 svobik7

@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

MichalZalecki avatar Aug 05 '20 20:08 MichalZalecki