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

Broken SSR: You should not use <Link> outside a <Router> when I'm clearly using <Link> inside a <Router> #6789

Closed
modsoussi opened this issue Jun 15, 2019 · 5 comments

Comments

@modsoussi
Copy link

modsoussi commented Jun 15, 2019

Version

V5.0.1

Test Case

https://github.com/modsoussi/react-ssr

Steps to reproduce

  1. git clone https://github.com/modsoussi/react-ssr.git
  2. cd react-ssr
  3. npm i
  4. npm start
  5. Go to localhost:5000/
  6. Check logs for invariant error

Expected Behavior

Expect app to render. This is the code for App:

class App extends React.Component {
  render() {
    return (
      <div>
        <nav>
          <ul>
            <li><Link to="/">Hello</Link></li>
            <li><Link to="/bye">Bye</Link></li>
          </ul>
        </nav>

        <Route path="/" exact component={Hello} />
        <Route path="/bye" component={Bye} />
      </div>
    )
  }
}

On the client, it's rendered inside a BrowserRouter, and on the server it's rendered inside a StaticRouter.

  • Client:
hydrate(
    <BrowserRouter>
      <App />
    </BrowserRouter>, document.getElementById('app-root'));
  • Server:
const component = require('./dist');
const context = {};

const app = React.createElement(
  StaticRouter,
  { location: req.url, context: context },
  React.createElement(component)
);
const markup = renderToStaticMarkup(app);

Actual Behavior

renderToStaticMarkup(app) fails and throws Error: Invariant failed: You should not use <Link> outside a <Router>.

@taddison
Copy link

Similar issue - fixed by downgrading react-router-dom to 5.0.0.

@modsoussi
Copy link
Author

Downgrading react-router-dom to 5.0.0 didn't work

@timdorr
Copy link
Member

timdorr commented Jun 16, 2019

OK, so you're using two instances of the Router context pair. (Side note: I'll explain things fully here, but I'm going to have to turn this into a saved response...)

There is a new React.createContext API that we use in 5.0 to replace the legacy context usage. This involves creating a "context" pair, a set of Provider and Consumer components. We create that in a singleton module that is imported into Router and Link directly. In this new API, you have to use the exact same instance. Two separately-created contexts will never match up and won't be visible to each other.

In your server, you're importing from your build output and from the react-router-dom module. This means that within your bundle, it's importing the bundle copy of context and in the server render, it's importing directly from the module (in node_modules). This results in the Provider in Router not being visible to the Consumer in Link.

How you solve this is up to you. The most ideal option is to not import your bundle, but import from src directly. (Note: that might involve using @babel/register if you're using JS features incompatible with your version of Node.) Another option is to export react-router-dom from your bundle and use that in your server renderer so that you have the same context pair. I can't tell you what is the best option, since you need to evaluate any tradeoffs.

One thing to add: This isn't fundamentally a React Router or even a React problem. Anything that uses a singleton module would be affected by this if you reference that singleton in your bundle and your server separately. It's the nature of JS itself, so the fixes I suggest are best for avoiding problems with other libraries as well.

@timdorr timdorr closed this as completed Jun 16, 2019
@Richacinas
Copy link

Take a look at my answer for a possible solution here:

#6769 (comment)

@modsoussi
Copy link
Author

Exporting StaticRouter from my bundle did the trick. I import it and use it in my server's code.

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

No branches or pull requests

4 participants