Skip to content

Instantly share code, notes, and snippets.

@RavenHursT
Last active September 30, 2022 03:10
Show Gist options
  • Save RavenHursT/715a3931c7ffc5a394bb55c7743ef961 to your computer and use it in GitHub Desktop.
Save RavenHursT/715a3931c7ffc5a394bb55c7743ef961 to your computer and use it in GitHub Desktop.
React .renderToStaticNodeStream() example w/ redux and react-router
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import getRootEpic from '../../store/epics/root.epic'
import {createEpicMiddleware} from 'redux-observable'
import reduxLogger from 'redux-logger'
import { Provider } from 'react-redux'
import MainApp from './main-app.component'
import { BrowserRouter } from 'react-router-dom'
import appReducers from '../../store/reducers'
import { CookiesProvider } from 'react-cookie'
const hydrateData = window.__HYDRATE_DATA__
delete window.__HYDRATE_DATA__
const store = createStore(
appReducers,
hydrateData,
applyMiddleware(reduxLogger, createEpicMiddleware(getRootEpic()))
)
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<CookiesProvider>
<MainApp />
</CookiesProvider>
</BrowserRouter>
</Provider>,
document
)
import React from 'react'
import { connect } from 'react-redux'
import HeadTag from '../head-tag'
import { Route, Switch } from 'react-router-dom'
import { withRouter } from 'react-router-dom'
import ProtectedRoute from '../protected-route'
import { toggleNav } from '../navigation/store/navigation.actions'
import ContentArea from '../content-area'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import AppBar from 'material-ui/AppBar'
import Navigation from '../navigation'
import SignIn from '../sign-in'
import {reducersMap} from '../../store/reducers/index'
import './styles/main-app.scss'
const getHydrateState = (props) => Object.keys(props)
.filter(key => Object.keys(reducersMap).includes(key))
.reduce((obj, key) => {
obj[key] = props[key]
return obj
}, {})
const MainApp = (props) => {
const muiTheme = props.serveRequest ? getMuiTheme(
{},
{
userAgent: props.serveRequest.headers[`user-agent`]
}
) : undefined
const hydrateData = JSON.stringify(getHydrateState(props))
return (
<html>
<HeadTag title={props.viewTitle} />
<body>
<MuiThemeProvider muiTheme={muiTheme} >
<Switch>
<Route
path="/sign-in"
exact={true}
component={SignIn} />
<Route
render={
({ staticContext }) =>
<div id="main-app">
<AppBar
title="HoneyCo: Sage"
onLeftIconButtonTouchTap={props.toggleNav}
style={{
backgroundColor: `#daa03e`
}}
/>
<div id="content-wrp">
<Switch>
<ProtectedRoute
path="/"
component={ContentArea} />
</Switch>
<Navigation open={props.navigation.navOpen}/>
</div>
</div>
}
/>
</Switch>
</MuiThemeProvider>
<script dangerouslySetInnerHTML={{__html: `window.__HYDRATE_DATA__ = ${hydrateData}`}} />
<script src="/static/js/bundle.js"></script>
</body>
</html>
)
}
const mapStateToProps = state => {
return { ...state }
}
export function mapDispatchToProps(dispatch) {
return {
toggleNav: () => {
dispatch(toggleNav())
}
}
}
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(MainApp)
)
import React from 'react'
import ReactDomServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { createStore, applyMiddleware } from 'redux'
import reduxLogger from 'redux-logger'
import { Provider } from 'react-redux'
import MainApp from '../components/main-app'
import getRootEpic from '../store/epics/root.epic'
import { createEpicMiddleware } from 'redux-observable'
import appReducer from '../store/reducers'
import stream from 'stream'
import { CookiesProvider } from 'react-cookie'
import Cookies from 'universal-cookie'
const docType = `<!DOCTYPE html>`
export default async function viewsMiddleware(ctx, next) {
if(!ctx.accepts([`html`, `text/html`])) {
await next()
return
}
const epicMiddleware = createEpicMiddleware(getRootEpic())
const pass = new stream.PassThrough()
const routeCtx = {}
const store = createStore(
appReducer,
applyMiddleware(reduxLogger, epicMiddleware)
)
ctx.type = `html`
ctx.body = pass
pass.write(docType)
console.dir(ctx.headers, {colors:true, depth:2})
const appStream = ReactDomServer.renderToStaticNodeStream(
<Provider store={store}>
<StaticRouter location={ctx.request.path} context={routeCtx}>
<CookiesProvider cookies={new Cookies(ctx.headers.cookie || ``)}>
<MainApp serveRequest={ctx.request} />
</CookiesProvider>
</StaticRouter>
</Provider>
)
appStream.pipe(pass, {end: false})
appStream.on(`end`, () => {
console.log(`routeCtx => `, routeCtx)
ctx.status = routeCtx.status || 200
if (routeCtx.url) {
ctx.status = 307
ctx.response.set(`Location`, routeCtx.url)
ctx.body = `Temporarily Moved`
}
pass.end()
})
await next()
}
@RavenHursT
Copy link
Author

I wrote this year's ago.. I'm sure there's better examples out there now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment