/redux-hermit

a server-side-rendering middleware.

Primary LanguageJavaScript

Redux Hermit

is watch the dispatched promise actions for server-side-rending.

Installation

npm install redux-hermit --save

Problem

currently ReactDOMServer.renderToString doesn't wait for the promise. the following code will fail.

// Foo.jsx
import React from 'react';
import { connect } from 'react-redux';

export default connect(
  state => state,
)(
  class extends React.Component {
    componentWillMount() {
      if (this.props.alreadyInitialized) {
        return;
      }
      // any asynchronous processing...
      console.log('mount props:', this.props);
      this.props.dispatch(new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            type: 'update',
            payload: {
              foo: 'complete',
            },
          });
        }, Math.random() * 500);
      }));
    }
    render() {
      return (
        <div>
          {this.props.foo || 'loading...'}
          {this.props.children}
        </div>
      );
    }
  }
);
// index.jsx
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import { Provider } from 'react-redux';
import Foo from './Foo';

const store = createStore(
  (state = {}, action) => {
    if (action.type === 'alreadyInitialized') {
      return Object.assign({}, state, { alreadyInitialized: true });
    }
    if (action.type === 'update') {
      return Object.assign({}, state, action.payload);
    }
    return state;
  },
  applyMiddleware(
    promiseMiddleware,
  ),
);

const provider = (
  <Provider store={store}>
    <Foo />
  </Provider>
);

console.log('state:', store.getState())
console.log(renderToStaticMarkup(provider));
babel-node index.jsx
# state: {}
# mount props: { dispatch: [Function] }
# <div>loading...</div>

inspired by this hack.

promiseWatchMiddleware.wait is wait the fulfill of captured promise actions at componentWillMount.

import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import createPromiseWatchMiddleware from 'redux-hermit';
import { Provider } from 'react-redux';
import Foo from './Foo';

const promiseWatchMiddleware = createPromiseWatchMiddleware();
const store = createStore(
  (state = {}, action) => {
    if (action.type === 'alreadyInitialized') {
      return Object.assign({}, state, { alreadyInitialized: true });
    }
    if (action.type === 'update') {
      return Object.assign({}, state, action.payload);
    }
    return state;
  },
  applyMiddleware(
    promiseWatchMiddleware,
    promiseMiddleware,
  ),
);

const provider = (
  <Provider store={store}>
    <Foo />
  </Provider>
);

renderToStaticMarkup(provider);

promiseWatchMiddleware.wait().then(() => {
  store.dispatch({ type: 'alreadyInitialized' });

  console.log('state:', store.getState());
  console.log(renderToStaticMarkup(provider));
});

you can server-side-rendering.

babel-node index.jsx
# state: { foo: 'complete' }
# <div>complete</div>

Attention

  • that you must run the twice render.
  • that also componentWillMount is performed twice(can interrupted by using the alreadyInitialized).

Development

Requirement global

  • NodeJS v5.7.0
  • Npm v3.7.1
git clone https://github.com/59naga/redux-hermit
cd redux-hermit
npm install

npm test

License

MIT