Skip to content

Instantly share code, notes, and snippets.

@tomatau
Last active December 2, 2017 21:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomatau/9c6011dcb5b9f357368aac2b3a2b1430 to your computer and use it in GitHub Desktop.
Save tomatau/9c6011dcb5b9f357368aac2b3a2b1430 to your computer and use it in GitHub Desktop.
SSR React Router Redux Suggestion
import { Switch, Route } from 'react-router'
import { replace } from 'react-router-redux'
@connect(null, { replace })
class PrivateRoute extends React.Component {
componentWillMount() {
this.props.replace('/foo')
}
render() {
return (
<h1>Private</h1>
)
}
}
export default class App extends React.Component {
render() {
return (
<div>
<Switch>
<Route path='/private' component={PrivateRoute} />
</Switch>
</div>
)
}
}
import invariant from 'invariant'
import { addLeadingSlash, createPath, parsePath } from 'history/PathUtils'
const noop = () => {}
const normalizeLocation = (object) => {
const { pathname = '/', search = '', hash = '' } = object
return {
pathname,
search: search === '?' ? '' : search,
hash: hash === '#' ? '' : hash,
}
}
const addBasename = (basename, location) => {
if (!basename)
return location
return {
...location,
pathname: addLeadingSlash(basename) + location.pathname,
}
}
const stripBasename = (basename, location) => {
if (!basename)
return location
const base = addLeadingSlash(basename)
if (location.pathname.indexOf(base) !== 0)
return location
return {
...location,
pathname: location.pathname.substr(base.length),
}
}
const createLocation = (location) =>
typeof location === 'string' ? parsePath(location) : normalizeLocation(location)
const createURL = (location) =>
typeof location === 'string' ? location : createPath(location)
const staticHandler = (methodName) => () => {
invariant(
false,
'You cannot %s with <StaticRouter>',
methodName
)
}
const createStaticHistory = (basename='', location='/') => {
const createHref = (path) =>
addLeadingSlash(basename + createURL(path))
const handlePush = (location) => {
history.context.action = 'PUSH'
history.context.location = addBasename(basename, createLocation(location))
history.context.url = createURL(history.context.location)
}
const handleReplace = (location) => {
history.context.action = 'REPLACE'
history.context.location = addBasename(basename, createLocation(location))
history.context.url = createURL(history.context.location)
}
const history = {
// put context on history for use in StaticRouter
context: {},
createHref: createHref,
action: 'POP',
location: stripBasename(basename, createLocation(location)),
push: handlePush,
replace: handleReplace,
go: staticHandler('go'),
goBack: staticHandler('goBack'),
goForward: staticHandler('goForward'),
listen: noop,
block: noop,
}
return history
}
export default createStaticHistory
import ReactDOMServer from 'react-dom/server'
import { Provider } from 'react-redux'
import StaticRouter from 'react-router'
import { routerMiddleware } from 'react-router-redux'
app.use(function handleRequest(ctx) {
const history = createStaticHistory(ctx.request.url)
const store = makeCreateStore([
...middleware,
routerMiddleware(history),
])(rootReducer, {})
const html = ReactDOMServer.renderToString(
<Provider store={store}>
<StaticRouter history={history}>
{reactApp}
</StaticRouter>
</Provider>
)
if (history.context.url) {
ctx.status = 302
ctx.redirect(history.context.url)
} else {
ctx.response.body = `<!doctype html>${
ReactDOMServer.renderToStaticMarkup(<Html html={html}/>)
}`
}
})
import React, { PropTypes } from 'react'
import { Router } from 'react-router'
import createStaticHistory from 'react-router/createStaticHistory'
/**
* The public top-level API for a "static" <Router>, so-called because it
* can't actually change the current location. Instead, it just records
* location changes in a context object. Useful mainly in testing and
* server-rendering scenarios.
*/
class StaticRouter extends React.Component {
static propTypes = {
history: PropTypes.object,
};
static defaultProps = {
history: createStaticHistory(),
};
static childContextTypes = {
router: PropTypes.object.isRequired,
}
getChildContext() {
return {
router: {
// context needed by history so moved into it
// this reference could get lost...
staticContext: this.props.history.context,
},
}
}
render() {
const { history, ...props } = this.props
return <Router {...props} history={history}/>
}
}
export default StaticRouter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment