Add SSR'd Portals example (#6365)

This commit is contained in:
Jess Telford 2019-02-21 00:06:28 +11:00 committed by Tim Neutkens
parent 369ac488e0
commit 9d7385c3d8
5 changed files with 169 additions and 0 deletions

View file

@ -0,0 +1,44 @@
# Example with Server Side Rendered portals
## How to use
### Using `create-next-app`
Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:
```bash
npx create-next-app --example with-portals-ssr with-portals-ssr
# or
yarn create next-app --example with-portals-ssr with-portals-ssr
```
### Download manually
Download the example:
```bash
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-portals-ssr
cd with-portals-ssr
```
Install it and run:
```bash
npm install
npm run dev
# or
yarn
yarn dev
```
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
```bash
now
```
## The idea behind the example
An example of Server Side Rendered React [Portals](https://reactjs.org/docs/portals.html) with
[`@jesstelford/react-portal-universal`](https://www.npmjs.com/package/@jesstelford/react-portal-universal)
and Next.js.

View file

@ -0,0 +1,13 @@
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@jesstelford/react-portal-universal": "1.0.0",
"next": "latest",
"react": "^16.8.0",
"react-dom": "^16.8.0"
}
}

View file

@ -0,0 +1,25 @@
import React from 'react'
import App, { Container } from 'next/app'
import { prepareClientPortals } from '@jesstelford/react-portal-universal'
if (process.browser) {
// On the client, we have to run this once before React attempts a render.
// Here in _app is a great place to do it as this file is only required once,
// and right now (outside the constructor) is before React is invoked.
prepareClientPortals()
}
class MyApp extends App {
render () {
const { Component, pageProps } = this.props
return (
<Container>
{/* This is where we'll render one of our universal portals */}
<div id='modal' />
<Component {...pageProps} />
</Container>
)
}
}
export default MyApp

View file

@ -0,0 +1,23 @@
import Document from 'next/document'
import { ServerPortal } from '@jesstelford/react-portal-universal/server'
export default class extends Document {
static async getInitialProps (ctx) {
const portals = new ServerPortal()
const originalRenderPage = ctx.renderPage
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => portals.collectPortals(<App {...props} />)
})
const { html, ...props } = await Document.getInitialProps(ctx)
const htmlWithPortals = portals.appendUniversalPortals(html)
return {
html: htmlWithPortals,
...props
}
}
}

View file

@ -0,0 +1,64 @@
import * as React from 'react'
import { UniversalPortal } from '@jesstelford/react-portal-universal'
export default class Index extends React.Component {
constructor () {
super(...arguments)
this.state = { opened: true }
}
open = () => {
this.setState({ opened: true })
}
close = () => {
this.setState({ opened: false })
}
render () {
return (
<React.Fragment>
{/* A portal that is adjacent to its target */}
<div id='target' />
<UniversalPortal selector='#target'>
<h1>Hello Portal</h1>
</UniversalPortal>
{/* Open a modal in a portal that is elsewhere in the react tree */}
<button type='button' onClick={this.open}>
Open Modal
</button>
{this.state.opened && (
<UniversalPortal selector='#modal'>
<div style={{
position: 'fixed',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
top: 0,
right: 0,
bottom: 0,
left: 0
}}>
<div style={{
backgroundColor: 'white',
position: 'absolute',
top: '10%',
right: '10%',
bottom: '10%',
left: '10%',
padding: '1em'
}}>
<p>
This modal is rendered using{' '}
<a href='https://www.npmjs.com/package/@jesstelford/react-portal-universal'><code>@jesstelford/react-portal-universal</code></a>.
</p>
<button type='button' onClick={this.close}>
Close Modal
</button>
</div>
</div>
</UniversalPortal>
)}
</React.Fragment>
)
}
}