Router: React Context Bug?

Created on 1 Jun 2018  路  4Comments  路  Source: reach/router

Here's a weird one:

Test case

mkdir test
cd test
yarn init
yarn add [email protected] [email protected] @reach/router
touch index.js

Then paste this into index.js

const React = require("react");
const { renderToString } = require("react-dom/server");
const { ServerLocation, Router } = require("@reach/router");

let { createElement: e, createContext } = React;

let Home = () => e("div", null, "Heyooooooo");

let html = () =>
  renderToString(
    e(ServerLocation, { url: "/" }, e(Router, null, e(Home, { path: "/" })))
  );

console.log("1st attempt", html());
console.log("2nd attempt", html());

Now run:

node index.js

And you'll get this:

1st attempt <div style="outline:none" tabindex="-1" role="group"><div>Heyooooooo</div></div>
2nd attempt <div style="outline:none" tabindex="-1" role="group"><div>Heyooooooo</div></div>

The bug

Now:

yarn add [email protected] [email protected]
node index.js

And you get this!

1st attempt <div style="outline:none" tabindex="-1" role="group"><div>Heyooooooo</div></div>
/Users/ryanflorence/Desktop/test/node_modules/@reach/router/lib/utils.js:243
  .replace(/(^\/+|\/+$)/g, "").split("/");
  ^

TypeError: Cannot read property 'replace' of undefined
    at segmentize (/Users/ryanflorence/Desktop/test/node_modules/@reach/router/lib/utils.js:243:3)
    at rankRoute (/Users/ryanflorence/Desktop/test/node_modules/@reach/router/lib/utils.js:226:35)
    at Array.map (<anonymous>)
    at rankRoutes (/Users/ryanflorence/Desktop/test/node_modules/@reach/router/lib/utils.js:235:17)
    at pick (/Users/ryanflorence/Desktop/test/node_modules/@reach/router/lib/utils.js:42:16)
    at RouterImpl.render (/Users/ryanflorence/Desktop/test/node_modules/@reach/router/index.js:231:33)
    at processChild (/Users/ryanflorence/Desktop/test/node_modules/react-dom/cjs/react-dom-server.node.development.js:2207:18)
    at resolve (/Users/ryanflorence/Desktop/test/node_modules/react-dom/cjs/react-dom-server.node.development.js:2064:5)
    at ReactDOMServerRenderer.render (/Users/ryanflorence/Desktop/test/node_modules/react-dom/cjs/react-dom-server.node.development.js:2349:22)
    at ReactDOMServerRenderer.read (/Users/ryanflorence/Desktop/test/node_modules/react-dom/cjs/react-dom-server.node.development.js:2323:19)

If you open up node_modules and change the source of @reach/router/index.js and add this to line 195 (inside of Router)

console.log(baseContext)

Run it again:

$ node index.js
{ baseuri: '/', basepath: '/' }
1st attempt <div style="outline:none" tabindex="-1" role="group"><div>Heyooooooo</div></div>
{ location: { pathname: '/' }, navigate: [Function: navigate] }

1st attempt runs just fine, but the second attempt the base context is different! And it's the Location context! 馃樀

So what's up?

No idea. The difference between 16.2 and 16.3 is that in 16.2 the createContext polyfill is being used, but in 16.3 it's using React's own createContext.

The fact that we're getting LocationContext being called back in BaseContext makes me think something is wrong with React.createContext. We only render BaseContext in one place, and we don't do any sort of singleton state with it (first run works, second doesn't) so it'd be really surprising if it's our code that's mixing up the context.

What can you do?

Try to reproduce the bug w/o Reach Router, or identify any misuse of context that causes the contexts to get mixed up.

Most helpful comment

Fixed in React 16.4.1.

All 4 comments

Thanks @zenios, we'll worry about this on the react issue tracker :)

Is there a workaround in the mean time?

Wait, I don't see how BaseContext ever updates?

The provider: https://github.com/reach/router/blob/master/src/index.js#L224
is inside the consumer: https://github.com/reach/router/blob/master/src/index.js#L156

From the docs: https://reactjs.org/docs/context.html

All Consumers that are descendants of a Provider will re-render whenever the Provider鈥檚 value prop changes.

I added the console.log(baseContext) as suggested, and I never see it update more than one.
I'm using preact, so its the polyfilled create-react-context

Fixed in React 16.4.1.

Was this page helpful?
0 / 5 - 0 ratings