javascriptreactjsreact-domserver-side-renderingreact-dom-server

Server-side rendering with React with a randomly generated string?


I'm trying server-side rendering for the first time in my React/Redux app. An issue I'm having right now is that I need the initial state to have a randomly generated string and then pass it as a prop to my main App component. This is obviously causing an issue because it's generating different strings for the client and server. Is there something I can do to stop this issue from happening?

Basic structure to help with understanding:

App.js

import React from 'react';
import { connect } from 'react-redux';

const App = ({ randomStr }) => (
  <div> 
    <p>{randomStr}</p>
  </div>
);

const mapStateToProps = (state) => ({
  ...
});

const mapDispatchToProp = (dispatch) => ({
  ...
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

And my reducer:

reducer.js

import { generateString } from '../util';
import { NEW_STRING } from '../constants';

const stringReducer = (state = generateString(), action) => {
  switch (action.type) {
    case NEW_STRING:
      return generateString();

    default:
      return state;
  }
};

export default stringReducer;

As you can see, I'm getting the randomStr from my redux store and rendering it, but it is different in client and server. Any help would be appreciated!


Solution

  • When you're generating the store and your initial state on the server side, the client should not do the same and try to regenerate the data.

    You need to generate your store and your initial state only once (on the server) and pass it down to the client. To do that, you need to inject it in the initial component you'll be rendering.

    The most common approach is to add a <script> tag and attach the state to your window, for instance in window.__initialState.

    For example, if you're using ReactDOM.renderToString on the server side to render your initial component called Html, you can:

    const html = ReactDOM.renderToString(<Html initialState={initialState} />);
    

    Then, in your Html component, you can inject this data so the client can use it later:

    <script dangerouslySetInnerHTML={{ __html: `window.__initialState=${JSON.stringify(this.props.initialState)};` }}/>
    

    After that, on the client side, you can use window.__initialState to get your data back and you'll end up with the same data on the server and the client.