Created
October 15, 2020 19:58
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'), | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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