Skip to content

Instantly share code, notes, and snippets.

@jarkkosyrjala
Created October 15, 2020 19:58
Show Gist options
  • Save jarkkosyrjala/b0139b60291b2ca22a7f0a52805529c3 to your computer and use it in GitHub Desktop.
Save jarkkosyrjala/b0139b60291b2ca22a7f0a52805529c3 to your computer and use it in GitHub Desktop.
Render static content as HTML and on hydration get the content from server side rendered HTML back as props
import * as React from 'react'
import DynamicAppWithStaticSections, {
DynamicAppWithStaticSectionsProps,
DynamicAppWithStaticSectionsHydrationProps,
} from './DynamicAppWithStaticSections'
import { hydrate } from 'react-dom'
interface AppWindow extends Window {
__APP__: DynamicAppWithStaticSectionsHydrationProps
}
declare const window: AppWindow
const partiallyStaticComponentProps: DynamicAppWithStaticSectionsProps = {
// Only part of the props are passed to JSON in DOM
...window.__APP__,
// Get the rendered html and pass it back as props
staticSections: Array.from(
document.querySelectorAll('.static-sections > section > div'),
(div) => div.innerHTML,
),
}
hydrate(
<DynamicAppWithStaticSections {...partiallyStaticComponentProps} />,
document.getElementById('app'),
)
import * as React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { useState } from 'react'
export interface DynamicAppWithStaticSectionsProps {
dynamicText: string
staticSections: string[]
}
export type DynamicAppWithStaticSectionsHydrationProps = Omit<
DynamicAppWithStaticSectionsProps,
'staticSections'
>
interface SectionWithStaticContentProps {
html: string
}
const SectionWithStaticContent: React.FC<SectionWithStaticContentProps> = ({ html }) => {
const [show, setShow] = useState<boolean>(true)
return (
<section>
<button onClick={() => setShow(!show)}>Toggle</button>
<div
style={{ display: show ? 'block' : 'none' }}
dangerouslySetInnerHTML={{ __html: html }}
/>
}
</section>
)
}
const DynamicAppWithStaticSections: React.FC<DynamicAppWithStaticSectionsProps> = ({
dynamicText,
staticSections,
}) => {
return (
<>
<div>{renderToStaticMarkup(<>{dynamicText}</>)}</div>
<div className="static-sections">
{staticSections.map((html) => (
<SectionWithStaticContent html={html} />
))}
</div>
</>
)
}
export default DynamicAppWithStaticSections
import * as React from 'react'
import { DynamicAppWithStaticSectionsProps } from './DynamicAppWithStaticSections'
export interface HtmlPageProps {
partiallyStaticComponent: DynamicAppWithStaticSectionsProps
}
/**
* Html template that prints the hydrations props as JSON
*/
const Html: React.FC<HtmlPageProps> = ({ partiallyStaticComponent, children }) => {
let { staticSections: _omit, ...hydrationProps } = partiallyStaticComponent
return (
<html>
<body>
<h1>Example</h1>
<div id="app">{children}</div>
<script
dangerouslySetInnerHTML={{
__html: `window.__APP__ = ${JSON.stringify(hydrationProps)}`,
}}
/>
</body>
</html>
)
}
export default Html
import { NextFunction, Request, Response } from 'express-serve-static-core'
import * as React from 'react'
import DynamicAppWithStaticSections, {
DynamicAppWithStaticSectionsProps,
} from './DynamicAppWithStaticSections'
import { renderToStaticMarkup, renderToString } from 'react-dom/server'
import Html from './Html'
const getPartiallyStaticComponentProps = async (): Promise<DynamicAppWithStaticSectionsProps> => {
// Fetch from server etc...
return Promise.resolve({
dynamicText: 'foo',
staticSections: ['Hello', 'World'],
})
}
const exampleExpressRouteHandler = async (
_req: Request,
res: Response,
_next: NextFunction,
): Promise<any> => {
// get the props from somewhere
const partiallyStaticComponentProps = await getPartiallyStaticComponentProps()
return res.send(
renderToString(
<Html partiallyStaticComponent={partiallyStaticComponentProps}>
<DynamicAppWithStaticSections
{...{
...partiallyStaticComponentProps,
staticSections: partiallyStaticComponentProps.staticSections.map((content) =>
renderToStaticMarkup(<div className="fancy-but-static-sub-component">{content}</div>),
),
}}
/>
</Html>,
),
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment