Add SSR'd Portals example (#6365)
This commit is contained in:
parent
369ac488e0
commit
9d7385c3d8
5 changed files with 169 additions and 0 deletions
44
examples/with-portals-ssr/README.md
Normal file
44
examples/with-portals-ssr/README.md
Normal 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.
|
13
examples/with-portals-ssr/package.json
Normal file
13
examples/with-portals-ssr/package.json
Normal 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"
|
||||
}
|
||||
}
|
25
examples/with-portals-ssr/pages/_app.js
Normal file
25
examples/with-portals-ssr/pages/_app.js
Normal 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
|
23
examples/with-portals-ssr/pages/_document.js
Normal file
23
examples/with-portals-ssr/pages/_document.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
64
examples/with-portals-ssr/pages/index.js
Normal file
64
examples/with-portals-ssr/pages/index.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue