1

I want to use react's server-side-render to only get the initial state for the user. subsequent to that , i'd prefer to use websockets/ajax/client side routing to handle the application.

This is what i have so far

 function handleRender(req,res){

    getInitialState(id_token)
      .then(function(initialState){
        const store = createStore(rootReducer,initialState)
        console.log('store.getState() ',store.getState())
        const html = renderToString(
          <Provider store={store}>
            <App/>
          </Provider>
        )
        res.send(renderFullPage(html,initialState))
      })
      .catch(function(err){
        console.log('error')
      })
  }

  const renderFullPage = (html,initialState) => {
    return `
      <!doctype html>
      <html>
        <head>
          <title>Redux Universal</title>
        </head>
        <body>
          <div id="app"><div>${html}</div></div>
          <script>
            window.__INITIAL_STATE__= ${JSON.stringify(initialState)}
          </script>
          <script src="/bundle.js"></script>
        </body>
      </html>`
  }

my client side index.js is pretty much standard

const initialState = window.__INITIAL_STATE__

export const store = createStore(rootReducer,initialState)


ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route path="/" component={App} />
      <Route path ="/project" component={Project} />
    </Router>
  </Provider>
  ,document.getElementById('app')
)

The above works well. when i navigate to /project on the client side. The Project component renders.

However , when i refresh the page at /project - i get a quick render of the / page before the client side router moves to /project.

Also , whenever i switch between routes , i hit the server again which i dont want.

i get that this should happen because refreshing hits the server again causing it to send back

<Provider store={store}>
    <App/>
  </Provider>

which renders the app component after which react-router renders the /project component again.

is there a way i can just get window.__INITIAL_STATE__ and initial HTML from the server without it having to send the html on every refresh/route change.

I do not want to hit the server on every route change which is what universal react does i believe.

The most important thing for me is to get the initial state for the user on page load. I prefer to have the app as an spa (only hit server for initial render/state and api requests)

7
  • Where is handleRender getting called? Commented Jul 8, 2016 at 15:23
  • "Also , whenever i switch between routes , i hit the server again which i dont want.". Please clarify this point for me. SPA with the react-router and the Link from it does not cause the window to refresh and does not " hit the server on every route change" Commented Jul 8, 2016 at 15:24
  • @idbehold - handlerender is set as an express middleware - app.use(handleRender) Commented Jul 8, 2016 at 15:28
  • @Yozi - the route change is hitting the server for some reason :( .. do you think its probably because i'm not rendering the router context along with the initial state? Commented Jul 8, 2016 at 15:30
  • @Kunkka, I think this may be a critical error or something like that, check it in console. Your client render looks correct, my about the same Commented Jul 8, 2016 at 15:51

1 Answer 1

1

I'm pretty sure you're missing the part where you match the server-side requested route with what you have in your react router.

import { RoutingContext, match } from 'react-router';
import createLocation from 'history/lib/createLocation';
import routes from './react-routes';

app.get('/*', (req, res, next) => {
  // create a "location" object from the requested url
  const location = createLocation(req.url);

  // match the location to the react routes we have
  match({ routes, location }, (err, redirect, renderProps) => {
    if (err) {
      console.error(err);
      return res.status(500).end('Internal server error');
    }

    if (!renderProps) return res.status(404).end('Not found');

    getInitialState(id_token).then((initialState) => {
      const store = createStore(rootReducer,initialState);
      const html = renderToString(
        <Provider store={store}>
          {() =>
            <RoutingContext {...renderProps} />
          }
        </Provider>
      );
      res.send(renderFullPage(html,initialState));
    });
  });
});

Check out this isomorphic redux example app. Not everything will apply in your case, but check out the server.js, client.js, and routes.js.

Sign up to request clarification or add additional context in comments.

3 Comments

Wouldnt this essentially mean i hit the server on every url request? Even for /projects?
@Kunkka so long as you're using the <Link> element for navigation around your site then no. The <Link> element will use your react router to change the content on the page. It will not do a full page load unless you explicitly refresh the page or open a link in a new tab.
@Kunkka for example, if you opened a new tab and then entered yoursite.com/foo then your server would generate and send the HTML for that page. If you then clicked on a <Link> that went to /bar then you should see the address bar update to reflect that navigation, however you will not be doing a full page load. Monitor your network traffic and you will see that no request to yoursite.com/bar was made.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.