When testing components with <Link>
s, for example in my answer to Recommended approach for route-based tests within routes of react-router, I often use the following pattern to get access to the current location
for testing purposes:
const renderInRouter = () => {
const history = createMemoryHistory();
const wrapper = render(
<Router history={history}>
<MyPage />
</Router>
);
return { ...wrapper, history };
}
This worked fine up to v5.3, but after upgrading to v6 I get:
FAIL ./demo.test.js
✕ works (205 ms)
● works
TypeError: Cannot read properties of undefined (reading 'pathname')
at Router (../packages/react-router/index.tsx:281:5)
...
This use-case isn't covered in the migration docs, v6 has no testing guides so far and, although the API reference does show that the history
prop is no longer expected:
interface RouterProps {
basename?: string;
children?: React.ReactNode;
location: Partial<Location> | string;
navigationType?: NavigationType;
navigator: Navigator;
static?: boolean;
}
it's not clear what the v6 equivalent is; I tried switching to navigator={history}
but still got the same error.
To reproduce, copy the following files into a new directory and run npm install
then npm test
:
package.json
{
"name": "router6-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"jest": {
"testEnvironment": "jsdom"
},
"babel": {
"presets": [
"@babel/preset-react"
]
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/preset-react": "^7.16.0",
"@testing-library/react": "^12.1.2",
"jest": "^27.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.0.0"
}
}
index.test.js
:
const { render } = require("@testing-library/react");
const { createMemoryHistory } = require("history");
const React = require("react");
const { Router } = require("react-router-dom");
it("used to work", () => {
render(
<Router history={createMemoryHistory()}></Router>
);
});
If you npm install --save-dev react-router@5
and run npm test
again, you can see that this passes in v5.
The error "Cannot read property 'pathname' of undefined" occurs when we don't set the to prop on a Link component in React router. To solve the error, set the to prop on the Link to the specific path, e.g. <Link to="/">Home</Link> .
The object-based route configuration may look familiar if you were using the react-router-config package in v5. In v6, this configuration format has been promoted to a first-class API in core and the react-router-config package will be deprecated.
To add the link in the menu, use the <NavLink /> component by react-router-dom . The NavLink component provides a declarative way to navigate around the application. It is similar to the Link component, except it can apply an active style to the link if it is active.
React Router v6 splits apart the history into multiple pieces, for this use case the relevant parts are the navigator and the location. This change is hinted at in Use useNavigate
instead of useHistory
, and you can see it in the definition of the Navigator
type used in the Router
props:
export declare type Navigator = Omit<History, "action" | "location" | "back" | "forward" | "listen" | "block">;
Just changing history={history}
to navigator={history}
still left the location
prop, from which the router was trying to access the pathname
(among other properties), undefined. To get the test working again, update the rendering as follows:
const { render } = require("@testing-library/react");
const { createMemoryHistory } = require("history");
const React = require("react");
const { Router } = require("react-router-dom");
it("works", () => {
const history = createMemoryHistory();
render(
<Router location={history.location} navigator={history}></Router>
);
});
Note that from React Router 6.4 history
is no longer included as a dependency, so if you hadn't explicitly installed it you will need to add it back in.
This is my solution to the issue, from a course I have been following on Udemy:
const { render } = require("@testing-library/react");
const { createBrowserHistory } = require("history");
const React = require("react");
const { unstable_HistoryRouter: HistoryRouter } = require("react-router-dom");
it("works", () => {
const history = createBrowserHistory();
render(
<HistoryRouter history={history}></HistoryRouter>
);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With