Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small problem with passing router state from server to client #60

Closed
DJCordhose opened this issue Sep 18, 2015 · 16 comments
Closed

Small problem with passing router state from server to client #60

DJCordhose opened this issue Sep 18, 2015 · 16 comments
Labels

Comments

@DJCordhose
Copy link

In beta3 when you render on the server side and pass state to the client to continue there, the router seems to be confused by what is passed as router state and render nothing. If I reset that router state on the client before rendering everything works fine:

const initialState = window.__INITIAL_STATE__;
initialState.router = undefined; // // need to reset router state otherwise nothing gets rendered, bug?
const store = configureStore(initialState);

Is this supposed to work without the reset?

@Furizaa
Copy link

Furizaa commented Sep 18, 2015

Works fine for me. What does your initialState.router look like before you set it to undefined?

@DJCordhose
Copy link
Author

Hey, @Furizaa, thanks for the help 👍

Looks pretty messed up, already on the server side after making JSON out of it. Turns out JSON.stringify creates nulls for the components. Data looks good before making JSON out of it. Strangely in the components array the first entry is undefined before reasonable component data begins.

window.__INITIAL_STATE__ = {"router":{"routes":[{"childRoutes":[{"from":"/","to":"/votes","path":"/"},{"path":"/","childRoutes":[{"path":"votes"},{"path":"votes/:id"},{"path":"login(/:redirect)"},{"path":"compose"},{"path":"*"}]}]},{"path":"/","childRoutes":[{"path":"votes"},{"path":"votes/:id"},{"path":"login(/:redirect)"},{"path":"compose"},{"path":"*"}]},{"path":"votes"}],"params":{},"location":{"pathname":"/votes","search":"","hash":"","state":null,"action":"POP","key":null},"components":[null,null,null]}};

@Furizaa
Copy link

Furizaa commented Sep 18, 2015

I can't seem to find anything suspicious in there. Mine looks basically the same. Out of ideas so far - sorry. If you want you could provide some of your relevant server-side and client-side code and I'll take a peek and that. Your configureStore would be interesting in particular.

This is mine if you want to compare (stripped down):

import { compose, createStore, applyMiddleware } from 'redux';
import asyncMiddleware from 'middleware/async';
import multiMiddleware from 'middleware/multi';
import fetchMiddleware from 'middleware/fetch';
import historyMiddleware from 'middleware/history';
import rootReducer from 'reducers';

export default function configureStore(router, initialState = {}) {
  const middlewares = [
    multiMiddleware,
    historyMiddleware,
    asyncMiddleware,
    fetchMiddleware,
  ];

  const toolChain = [
    applyMiddleware(...middlewares),
    router,
  ];

  const store = compose(...toolChain)(createStore)(rootReducer, initialState);

  if (module.hot) {
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers/index');
      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
}

Server:

import configureStore from 'stores';
import createRoutes from 'routes';
import { reduxReactRouter, match } from 'redux-react-router/server';
const store = configureStore(reduxReactRouter({ getRoutes: createRoutes }), initialState);

store.dispatch(match(......

Client:

import React from 'react';
import ReactDOM from 'react-dom';
import createHistory from 'history/lib/createBrowserHistory';
import configureStore from 'stores';
import { reduxReactRouter } from 'redux-react-router';

const store = configureStore(reduxReactRouter({ createHistory }), window.__INIT__);

ReactDOM.render ...

@DJCordhose
Copy link
Author

Looks very similar to my stuff, I will try to create a small example that also shows the behavior...

@mwilc0x
Copy link

mwilc0x commented Sep 19, 2015

@Furizaa what does createRoutes do? I'm seeing the same issue as @DJCordhose. I saw something mentioned about getRoutes in latest release, but I am not entirely clear how that is supposed to work.

@Furizaa
Copy link

Furizaa commented Sep 19, 2015

It's just a closure that returns the routes. I'm not very good at explaining these thinks but if you want to know why thats the way it is, please look here: #50

It looks something like this:

// (Please note that I did not had the chance to test this snippet) 
export default function createRoutes(store) {
  checkAuth(nextState, replaceState) {
    const state = store.getState();
    if (!state.auth.token) {
      replaceState(null, '/');
    }
  } 
  return (
    <Route component={Layout}>
      <Route path="/admin" onEnter={this.checkAuth} ... />
      ...
    </Route>
  );
}

I'll try to come up wit a complete universal example soon.

@mwilc0x
Copy link

mwilc0x commented Sep 19, 2015

Thanks @Furizaa.

Ok, so I think I figured this out. If you pass routes to the ReduxRouter component as well as reduxReactRouter this seems to solve the issue with components being undefined on client side for initial render. @acdlite does this sound correct?

e.g.

ReactDOM.render(
  <Provider store={store}>
      <ReduxRouter routes={routes} />
  </Provider>,
  document.getElementById('app')
);
import {createStore, applyMiddleware, compose} from 'redux';
import {reduxReactRouter as clientRouter} from 'redux-router';
import {reduxReactRouter as serverRouter} from 'redux-router/server';
import routes from '../routes';
import createHistory from 'history/lib/createMemoryHistory';
import rootReducer from '../reducers/reducers';
import apiMiddleware from '../middleware/apiMiddleware';

export function configureStore(target, initialState) {
  let router;

  if (target === 'client') {
    router = clientRouter({routes, createHistory});
  } else if (target === 'server') {
    router = serverRouter({routes});
  }

  return compose(
    applyMiddleware(
      apiMiddleware
    ),
    router
  )(createStore)(rootReducer, initialState);
}

@DJCordhose
Copy link
Author

@mjw56 right! Thanks for figuring this out. If I additionally pass the routes as a property to the ReduxRouter component things work fine. Like this:

    const routes = getRoutes(store.getState);
    const html = renderToString(
      <Provider store={store}>
        <ReduxRouter routes={routes}/>
      </Provider>
    );

I guess this is a bug, because why would I have to supply them twice?

@Furizaa
Copy link

Furizaa commented Sep 19, 2015

Actually only the server reduxReactRouter needs the routes or a getRoutes closure. The <ReduxRouter /> component will need the routes on the client and the server. So if you remove them it should work too.

@HriBB
Copy link

HriBB commented Sep 20, 2015

I was able to figure out how to implement server rendering from tests

@synthresin
Copy link

I'm experiencing similar issue #90 in my case client couldn't process server derived initial state. either making initialState.router undefined or assigning routes again to client <ReduxRouter/> solved the problem. but I think there is something should be fixed in the architecture.

@synthresin
Copy link

@DJCordhose if you use serialize-javascript instead of JSON.stringfy, you can get components array properly. but It won't work because client store cannot read that value properly....(this is my case). so I did same workaround..(make initialState.router undefined or reassign routes to ReduxRouter

@colinmeinke
Copy link

I too am rendering server side, which works fine, but then when react kicks in on the client it wipes all components.

Like mentioned above I can get it working by adding the routes prop to ReduxRouter in the client JS, but this means I get immediate state changes on initial load.

Redux dev tools

Console

@Scarysize
Copy link
Contributor

Is this still an issue?

@bruno12mota
Copy link

bruno12mota commented Apr 29, 2016

I believe this is still an issue. I have an authenticate hook on routes onEnter and when I don't use the hack:

const state = window.__initialState;
state.router = undefined;

It outputs a _classCallCheck is undefined error

@datapimp
Copy link

datapimp commented Sep 8, 2016

I still have this issue as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants