-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Hi Jeremy, could you post a version of this with a working example? The project I'm building is using React Router v4, which doesn't seem have syncHistory as a working function. I think I almost have this hooked up, but am getting caught up on the middleware when creating my store. I've posted an example below that forces the use of your taxi middleware regardless of environment.
I'm also getting an empty value when I instantiate ReduxTaxi(). Because I handle a universal js implementation a little differently, I'm trying to pass the reduxTaxi object around to different files without completely understanding what I am passing. These errors are in both my development and production environments. Please take a look if you get a chance. Thank you!
Here's the code - used from several boilerplates that I've modified to do what I want:
ssr.js:
// Node Modules
import fs from 'fs';
import {basename, join} from 'path';
// Libraries
import React from 'react';
import {renderToString} from 'react-dom/server';
// Redux
import createStore from 'universal/redux/createStore.js';
import createHistory from 'history/createMemoryHistory'
import { ReduxTaxi } from 'redux-taxi';
// Components
import Html from './Html.js';
function renderApp(url, res, store, assets, reduxTaxi) {
const context = {};
const html = renderToString(
<Html
title='Test Site'
store={store}
url={url}
context={context}
assets={assets}
reduxTaxi={reduxTaxi} />
);
res.send('<!DOCTYPE html>'+html);
}
export const renderPage = function (req, res) {
const reduxTaxi = ReduxTaxi();
const history = createHistory( );
const store = createStore(history, reduxTaxi);
const assets = require('../../build/assets.json');
assets.manifest.text = fs.readFileSync(
join(__dirname, '..', '..', 'build', basename(assets.manifest.js)),
'utf-8'
);
renderApp(req.url, res, store, assets, {reduxTaxi});
};
export const renderDevPage = function (req, res) {
const reduxTaxi = ReduxTaxi();
const history = createHistory( );
const store = createStore(history, reduxTaxi);
renderApp(req.url, res, store, {reduxTaxi});
};
export default renderPage;Html.js:
// Libraries
import React, { Component } from 'react';
import {StaticRouter} from 'react-router';
import {renderToString} from 'react-dom/server';
import PropTypes from 'prop-types';
// Redux
import { Provider } from 'react-redux';
import {ReduxTaxi, ReduxTaxiProvider} from 'redux-taxi';
import createStore from '../universal/redux/createStore'; // Your store configurator
import promise from 'es6-promise';
promise.polyfill();
import { I18nextProvider } from 'react-i18next';
import i18n from '../i18n.server';
class Html extends Component {
static propTypes = {
url: PropTypes.string.isRequired,
store: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
assets: PropTypes.object
}
render () {
const PROD = process.env.NODE_ENV === 'production';
const {
title,
store,
assets,
url,
context,
reduxTaxi
} = this.props;
const {
manifest,
app,
vendor
} = assets || {};
let state = store.getState();
const initialState = `window.__INITIAL_STATE__ = ${JSON.stringify(state)}`;
const Layout = PROD ? require( '../../build/prerender.js') : () => {};
const initialComponent = (
<I18nextProvider i18n={ i18n }>
<Provider store={store}>
<ReduxTaxiProvider reduxTaxi={reduxTaxi}>
<StaticRouter location={url} context={context}>
<Layout />
</StaticRouter>
</ReduxTaxiProvider>
</Provider>
</I18nextProvider>
);
const root = PROD && renderToString(initialComponent);
//Does this have to be a different instantiation from what is passed in the props?
const allPromises = reduxTaxi.getAllPromises();
if (allPromises.length) {
// If we have some promises, we need to delay server rendering
promise
.all(allPromises)
.then(() => {
return (
<html>
<head>
<meta charSet="utf-8"/>
<title>{title}</title>
{PROD && <link rel="stylesheet" href="/static/prerender.css" type="text/css" />}
</head>
<body>
<script dangerouslySetInnerHTML={{__html: initialState}} />
{PROD ? <div id="root" dangerouslySetInnerHTML={{__html: root}}></div> : <div id="root"></div>}
{PROD && <script dangerouslySetInnerHTML={{__html: manifest.text}}/>}
{PROD && <script src={vendor.js}/>}
<script src={PROD ? app.js : '/static/app.js'} />
</body>
</html>
);
})
.catch(() => {
return {"error": "An error occurred"}
})
} else {
// otherwise, we can just respond with our rendered app
return (
<html>
<head>
<meta charSet="utf-8"/>
<title>{title}</title>
{PROD && <link rel="stylesheet" href="/static/prerender.css" type="text/css" />}
</head>
<body>
<script dangerouslySetInnerHTML={{__html: initialState}} />
{PROD ? <div id="root" dangerouslySetInnerHTML={{__html: root}}></div> : <div id="root"></div>}
{PROD && <script dangerouslySetInnerHTML={{__html: manifest.text}}/>}
{PROD && <script src={vendor.js}/>}
<script src={PROD ? app.js : '/static/app.js'} />
</body>
</html>
);
}
}
export default Html;createstore.js:
import {
createStore,
combineReducers,
applyMiddleware,
compose,
} from 'redux';
import {
ConnectedRouter,
routerReducer,
routerMiddleware,
syncHistory
} from 'react-router-redux';
import {
ReduxTaxiMiddleware,
PromiseMiddleware
} from 'redux-taxi';
import thunk from 'redux-thunk';
import {ReduxTaxi} from 'redux-taxi';
import * as Reducers from './reducers/index.js';
export default (history, instance) => {
const middleware = [
routerMiddleware(history),
ReduxTaxiMiddleware(instance.reduxTaxi),
PromiseMiddleware,
thunk
];
const store = compose(createStore(combineReducers({
...Reducers,
router: routerReducer
}), applyMiddleware(...middleware)));
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('./reducers', () => {
const nextReducers = require('./reducers/index.js');
const rootReducer = combineReducers({
...nextReducers,
router: routerReducer
});
store.replaceReducer(rootReducer);
});
}
return store;
}