6

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!

Saad
  • 49,729
  • 21
  • 73
  • 112
  • what's the code of generateString ? – y_nk Jul 21 '16 at 06:40
  • 2
    "Things you should never do inside a reducer: Mutate its arguments; Perform side effects like API calls and routing transitions; **Call non-pure functions**, e.g. Date.now() or Math.random()." And `generateString` that takes no arguments and returns a string is either a constant or impure. – Yury Tarabanko Jul 21 '16 at 06:41
  • 1
    Oh, I see what you're saying. I fixed my code so that i'm not using `generateString()` inside the reducer anymore, i do it as the initial state when I create the store. However, I still have the issue that it's generating differently for the client and server, so that when it is passed down as a prop it is [causing issues](http://i.imgur.com/KCdb44i.png). Is there any way around this? – Saad Jul 21 '16 at 21:49
  • If you're creating the store (and its initial state) on the server, then why is it regenerating on the client? The server's version of the store should be getting passed to the client, along with the generated string. – Matis Lepik Jul 23 '16 at 20:52
  • @meh_programmer just push the initial state you generated on the server to the client. – mash Jul 24 '16 at 01:28

1 Answers1

5

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.

HiDeoo
  • 10,353
  • 8
  • 47
  • 47