Skip to content

Example of implementation #4

@dougsmith1000

Description

@dougsmith1000

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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions