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.

Oldest comments (3)

Collapse
 
mikingtheviking profile image
MikingTheViking

That worked perfectly for me! Thanks for the writeup!

Collapse
 
charlie763 profile image
Charlie Wisoff • Edited

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 😕