Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Cannot read properties of undefined (reading 'pathname')" when testing pages in the v6 React Router

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.

like image 789
jonrsharpe Avatar asked Nov 05 '21 21:11

jonrsharpe


People also ask

How do you read pathname in react?

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> .

What is new in react v6 router?

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.

How do you define a link in react JS?

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.


2 Answers

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.

like image 136
jonrsharpe Avatar answered Oct 13 '22 06:10

jonrsharpe


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>
  );
});
like image 1
Gaurav Singh Avatar answered Oct 13 '22 07:10

Gaurav Singh