Skip to content

Commit

Permalink
Change SSR Fixture to use Partial Hydration
Browse files Browse the repository at this point in the history
This requires the enableSuspenseServerRenderer flag to be manually enabled
for the build to work.
  • Loading branch information
sebmarkbage committed Feb 10, 2019
1 parent e144a5e commit eb3ea2d
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 23 deletions.
39 changes: 27 additions & 12 deletions fixtures/ssr/src/components/App.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import React, {Component} from 'react';
import React, {useContext, useState, Suspense} from 'react';

import Chrome from './Chrome';
import Page from './Page';
import Page2 from './Page2';
import Theme from './Theme';

export default class App extends Component {
render() {
return (
<Chrome title="Hello World" assets={this.props.assets}>
<div>
<h1>Hello World</h1>
<Page />
</div>
</Chrome>
);
}
function LoadingIndicator() {
let theme = useContext(Theme);
return <div className={theme + '-loading'}>Loading...</div>;
}

export default function App({assets}) {
let [CurrentPage, switchPage] = useState(() => Page);
return (
<Chrome title="Hello World" assets={assets}>
<div>
<h1>Hello World</h1>
<a className="link" onClick={() => switchPage(() => Page)}>
Page 1
</a>
{' | '}
<a className="link" onClick={() => switchPage(() => Page2)}>
Page 2
</a>
<Suspense fallback={<LoadingIndicator />}>
<CurrentPage />
</Suspense>
</div>
</Chrome>
);
}
24 changes: 24 additions & 0 deletions fixtures/ssr/src/components/Chrome.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,27 @@ body {
padding: 0;
font-family: sans-serif;
}

body.light {
background-color: #FFFFFF;
color: #333333;
}

body.dark {
background-color: #000000;
color: #CCCCCC;
}

.light-loading {
margin: 10px 0;
padding: 10px;
background-color: #CCCCCC;
color: #666666;
}

.dark-loading {
margin: 10px 0;
padding: 10px;
background-color: #333333;
color: #999999;
}
12 changes: 10 additions & 2 deletions fixtures/ssr/src/components/Chrome.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React, {Component} from 'react';

import Theme, {ThemeToggleButton} from './Theme';

import './Chrome.css';

export default class Chrome extends Component {
state = {theme: 'light'};
render() {
const assets = this.props.assets;
return (
Expand All @@ -14,13 +17,18 @@ export default class Chrome extends Component {
<link rel="stylesheet" href={assets['main.css']} />
<title>{this.props.title}</title>
</head>
<body>
<body className={this.state.theme}>
<noscript
dangerouslySetInnerHTML={{
__html: `<b>Enable JavaScript to run this app.</b>`,
}}
/>
{this.props.children}
<Theme.Provider value={this.state.theme}>
{this.props.children}
<div>
<ThemeToggleButton onChange={theme => this.setState({theme})} />
</div>
</Theme.Provider>
<script
dangerouslySetInnerHTML={{
__html: `assetManifest = ${JSON.stringify(assets)};`,
Expand Down
15 changes: 14 additions & 1 deletion fixtures/ssr/src/components/Page.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
.bold {
.link {
font-weight: bold;
cursor: pointer;
}
.light-box {
margin: 10px 0;
padding: 10px;
background-color: #CCCCCC;
color: #333333;
}
.dark-box {
margin: 10px 0;
padding: 10px;
background-color: #333333;
color: #CCCCCC;
}
20 changes: 14 additions & 6 deletions fixtures/ssr/src/components/Page.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, {Component} from 'react';

import Theme from './Theme';
import Suspend from './Suspend';

import './Page.css';

const autofocusedInputs = [
Expand All @@ -14,17 +17,22 @@ export default class Page extends Component {
};
render() {
const link = (
<a className="bold" onClick={this.handleClick}>
<a className="link" onClick={this.handleClick}>
Click Here
</a>
);
return (
<div>
<p suppressHydrationWarning={true}>A random number: {Math.random()}</p>
<p>Autofocus on page load: {autofocusedInputs}</p>
<p>{!this.state.active ? link : 'Thanks!'}</p>
{this.state.active && <p>Autofocus on update: {autofocusedInputs}</p>}
<div className={this.context + '-box'}>
<Suspend>
<p suppressHydrationWarning={true}>
A random number: {Math.random()}
</p>
<p>Autofocus on page load: {autofocusedInputs}</p>
<p>{!this.state.active ? link : 'Thanks!'}</p>
{this.state.active && <p>Autofocus on update: {autofocusedInputs}</p>}
</Suspend>
</div>
);
}
}
Page.contextType = Theme;
15 changes: 15 additions & 0 deletions fixtures/ssr/src/components/Page2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, {useContext} from 'react';

import Theme from './Theme';
import Suspend from './Suspend';

import './Page.css';

export default function Page2() {
let theme = useContext(Theme);
return (
<div className={theme + '-box'}>
<Suspend>Content of a different page</Suspend>
</div>
);
}
21 changes: 21 additions & 0 deletions fixtures/ssr/src/components/Suspend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
let promise = null;
let isResolved = false;

export default function Suspend({children}) {
// This will suspend the content from rendering but only on the client.
// This is used to demo a slow loading app.
if (typeof window === 'object') {
if (!isResolved) {
if (promise === null) {
promise = new Promise(resolve => {
setTimeout(() => {
isResolved = true;
resolve();
}, 6000);
});
}
throw promise;
}
}
return children;
}
25 changes: 25 additions & 0 deletions fixtures/ssr/src/components/Theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, {createContext, useContext, useState} from 'react';

const Theme = createContext('light');

export default Theme;

export function ThemeToggleButton({onChange}) {
let theme = useContext(Theme);
let [targetTheme, setTargetTheme] = useState(theme);
function toggleTheme() {
let newTheme = theme === 'light' ? 'dark' : 'light';
// High pri, responsive update.
setTargetTheme(newTheme);
// Perform the actual theme change in a separate update.
setTimeout(() => onChange(newTheme), 0);
}
if (targetTheme !== theme) {
return 'Switching to ' + targetTheme + '...';
}
return (
<a className="link" onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} theme
</a>
);
}
5 changes: 3 additions & 2 deletions fixtures/ssr/src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import {hydrate} from 'react-dom';
import {unstable_createRoot} from 'react-dom';

import App from './components/App';

hydrate(<App assets={window.assetManifest} />, document);
let root = unstable_createRoot(document, {hydrate: true});
root.render(<App assets={window.assetManifest} />);

0 comments on commit eb3ea2d

Please sign in to comment.