Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server rendering react-router v4 passthrough if 404

In react-router v3 we could know when server side rendering had not matched against the current url. This allowed me to pass the request to my express.static middleware instead of sending the rendered App.

In react-router v4, we must use

    const htmlData = renderToString(
        <StaticRouter
            location={req.url}
            context={context}
        >
            <App/>
        </StaticRouter>
    );

in order to render on the server side. However, it automatically redirects everything to /. Why does this behavior even exist? Couldn't we just have an error like we expect insted of it silently failing?

How could I know that nothing matched so that I can call next() and have the other express's routes do the job?

Here is the whole function which I'd like to use:

app.get('*', (req, res, next) => {
    const context = {};
    const htmlData = renderToString(
        <StaticRouter
            location={req.url}
            context={context}
        >
            <App/>
        </StaticRouter>
    );

    console.log(JSON.stringify(context, null, 4)); // empty object
    if (context.url) { // <------------------------ Doesn't work (taken from example and thought it would contain the unmatched url)
        winston.info(`Passing ${req.url} along`);
        next(); // <--------------- Never called even if no route matches.
    } else {
        res.send(pageContent.replace('<div id="main"></div>',
            `<div id="main">${htmlData}</div>`));
    }
});

I tried doing stuff based on this but the // somewhere else is so precise, I couldn't understand it at all.

Here is my last attempt in case it is of any help. This is the Router.jsx file where I plan to define all my Routes.

import React from 'react';
import PropTypes from 'prop-types';
import {
    BrowserRouter,
    Route,
} from 'react-router-dom';
import App from './components/App.jsx';

export const Status = ({ code, children }) => (
    <Route render={({ staticContext }) => {
        if (staticContext) {
            staticContext.status = code;
        }
        return children;
    }}/>
);

Status.propTypes = {
    code     : PropTypes.number.isRequired,
    children : PropTypes.node.isRequired,
};

export const NotFound = () => (
    <Status code={404}>
        <div>
            <h1>Sorry, can’t find that.</h1>
        </div>
    </Status>
);

class Router extends React.Component {
    render() {
        return (
            <BrowserRouter>
                <div>
                    <Route exact path="/" component={App}/>
                    <Route component={NotFound}/>
                </div>
            </BrowserRouter>
        );
    }
}

export default Router;

(I know this doesn't make any sense at all since StaticRouter directly uses App without caring about Router.jsx but I have no Route at all inside App so I don't really understand how to do things, I guess.

like image 961
Telokis Avatar asked Oct 29 '22 06:10

Telokis


1 Answers

You should put NotFoundPage component route logic inside your App.jsx file, not Route.jsx which you are not using at all.

<Switch>
    <Route exact path="/" component={AppRootComponent}/>
    <Route component={NotFound}/>
</Switch>

In addition to this, this tutorial code is a great reference for server-side rendering using react router v4.

like image 173
sam Avatar answered Nov 09 '22 17:11

sam