DEV Community 👩‍💻👨‍💻

Kevin Sullivan
Kevin Sullivan

Posted on

React Router: testing "location.state"

Background

In the react channel on Spectrum, someone asked how to test that a component displayed data provided by location.state from react-router-dom. I had never done this before, so I sought to find out how to do it.

After some investigation, and trial and error, I came up with the following...

import * as React from "react";
import { render } from "@testing-library/react";
import { Router } from "react-router-dom";
import { createMemoryHistory } from "history";
import { App } from "./App";

it("renders location state", () => {
  const history = createMemoryHistory();
  const state = { a: 123, b: 456 }
  history.push("/", state);

  const { getByText } = render(
    <Router history={history}>
      <App />
    </Router>
  );

  getByText(state.a);
  getByText(state.b);
});
Enter fullscreen mode Exit fullscreen mode

Other Interesting Tidbits

  • <MemoryRouter history={createMemoryHistory(...)}> doesn't work

  • <BrowserRouter history={createBrowserHistory(...)}> does work, but doesn't typecheck

  • According to the docs

Note: Location state is only supported in createBrowserHistory and createMemoryHistory.

and here

location.state - Some extra state for this location that does not reside in the URL (supported in createBrowserHistory and createMemoryHistory)

Conclusion

So, this seems to work. It passes the tests.
Know of a better way? See any problems? Let me know, thanks.

Top comments (3)

Collapse
 
charlie763 profile image
Charlie Wisoff • Edited on

That sort of worked for me, but I still had code in my component that was throwing an error "cannot access property .state of null value", in this case the null value was location.

I did come up with a solution, which is to first grab the history of the component I'm rendering to test and then rerender that component with a location prop equal to history.location. Looking like:

let { getByTestId, history, rerender } = renderWithRouter(<Component 
    {...mockProps} />, {route: '/resources', state: {resourceId: 1}}
)
rerender(<Component {...mockProps} location={history.location}/>)
Enter fullscreen mode Exit fullscreen mode

That way, my code in the <Component/> that calls this.props.location.state doesn't fail.

Sidenote: renderWithRouter is a utility function I built that uses similar logic to what you have above and returns history along with ...render(<Component />

Collapse
 
moshfeu profile image
Mosh Feuchtwanger

Thanks for the post!
A heads up: eslint will throw

'history' should be listed in the project's dependencies. Run 'npm i -S history' to add iteslintimport/no-extraneous-dependencies

Don't do it - stackoverflow.com/a/66787971/863110.

How to solve this beside make eslint ignoring the line? I have no idea 😕

Collapse
 
mikingtheviking profile image
MikingTheViking

That worked perfectly for me! Thanks for the writeup!

Git push

Stop by this week's meme thread!