React Router is not changing component

308 Views Asked by At

I am trying to build a server-side-rendering app using react JS + Razzle + Redux. So I created two routes as follows.

    <StaticRouter history={history}>
      <Switch>
          <Route exact path="/" component={Home} />
          <Route exact path="/test" component={HomeTest} />
      </Switch>
    </StaticRouter>

I have two routes currently, one is home and second is test, the problem that I am facing is, Its not changing the routes its just showing home only

I have already tried both options Router and StaticRouter as well.

Please advice.

server/index.js

import App from '../common/containers/App';
import { Provider } from 'react-redux';
import React from 'react';
import configureStore from '../common/store/configureStore';
import express from 'express';
import { fetchCounter } from '../common/api/counter';
import qs from 'qs';
import { renderToString } from 'react-dom/server';
import serialize from 'serialize-javascript';

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

const server = express();

server
  .disable('x-powered-by')
  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))
  .get('/*', (req, res) => {
    fetchCounter(apiResult => {
      // Read the counter from the request, if provided
      const params = qs.parse(req.query);
      const counter = parseInt(params.counter, 10) || apiResult || 0;

      // Compile an initial state
      const preloadedState = { counter };

      // Create a new Redux store instance
      const store = configureStore(preloadedState);

      // Render the component to a string
      const markup = renderToString(
        <Provider store={store}>
          <App />
        </Provider>
      );

      // Grab the initial state from our Redux store
      const finalState = store.getState();

      res.send(`<!doctype html>
    <html lang="">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta charSet='utf-8' />
        <title>Razzle Redux Example</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        ${assets.client.css
          ? `<link rel="stylesheet" href="${assets.client.css}">`
          : ''}
          ${process.env.NODE_ENV === 'production'
            ? `<script src="${assets.client.js}" defer></script>`
            : `<script src="${assets.client.js}" defer crossorigin></script>`}
    </head>
    <body>
        <div id="root">${markup}</div>
        <script>
          window.__PRELOADED_STATE__ = ${serialize(finalState)}
        </script>
    </body>
</html>`);
    });
  });

export default server;

client/index.js

import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from '../common/store/configureStore';
import App from '../common/containers/App';

import '../assets/sass/App.scss';
import '../assets/sass/application.scss'
import 'semantic-ui-css/semantic.min.css';

const store = configureStore(window.__PRELOADED_STATE__);

hydrate(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

if (module.hot) {
  module.hot.accept('../common/containers/App', () => {
    hydrate(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
  });
}

App.js

import React from 'react';
import Counter from '../components/Counter';
import Home from '../../pages/Home/Home';
import HomeTest from '../../pages/Home/HomeTest';
import { StaticRouter, Route, Switch } from 'react-router-dom';

import { history } from '../../pages/utils/history'

const App = () => (
    <StaticRouter history={history}>
      <Switch>
          <Route exact path="/" component={Home} />
          <Route exact path="/test" component={HomeTest} />
      </Switch>
    </StaticRouter>
);

export default App;

history.js

import { createMemoryHistory } from 'history'

export const history = createMemoryHistory()

Home

import React from 'react';
const Home = () => {
    return (
        <main className="main-wrapper">
            <h2>Home 1</h2>
        </main>
    );
}
export default Home;

HomeTest

import React from 'react';
const HomeTest = () => {
    return (
        <main className="main-wrapper">
            <h2>Home 2</h2>
        </main>
    );
}
export default HomeTest;

Thanks

2

There are 2 best solutions below

1
On

Not sure if that's the problem but you have 2 imports of 'react-router-dom' in App.js:

import { Router } from 'react-router-dom'; // <- #1
import Counter from '../components/Counter';
import Home from '../../pages/Home/Home';
import HomeTest from '../../pages/Home/HomeTest';
import { Route, Switch } from 'react-router-dom'; // <- #2

Try to combine them in a single call ?

0
On

You have to use both BrowserRouter and StaticRouter. Edit your files as below, and it should work :)

App.js

const App = () => (
    // <StaticRouter history={history}> /* remove this */
      <Switch>
          <Route exact path="/" component={Home} />
          <Route exact path="/test" component={HomeTest} />
      </Switch>
    // </StaticRouter> /* remove this */
);

export default App;

server/index.js

const context: StaticRouterContext = {}; // add this
const markup = renderToString(
  <Provider store={store}>
    <StaticRouter location={req.originalUrl} context={context}> 
       <App />
    </StaticRouter>
  </Provider>
);

client/index.js

hydrate(
  <Provider store={store}>
    <BrowserRouter> 
       <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

If you want to understand more about this setup, watch this video: https://www.youtube.com/watch?v=dSO3GUL7JYE&list=PLMhLdUN2ZKJ2f-QDFBP1iphsmPd81MQOO&index=24&t=2s&ab_channel=BeALearner