Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useLoaderData must be used in a data router - Vitest - npm run test

In attempting to test React components that implement the React Router API, I'm getting an error while running tests at the same time. If either test is commented out the error is not raised and the other test passes.

  • Error: useLoaderData must be used within a data router.
afterEach(cleanup);

test(
    `Verify that <Timestamp /> displays both the UNIX and UTC dates for a date provided in milliseconds`, () => {

        vi.mock("react-router-dom", async () => {
            const module = await vi.importActual("react-router-dom");
            return {
                ...module,
                useLoaderData: vi.fn(() => {
                    return {unix: 7728271946, utc: "Tue, 31 Mar 1970 10:44:31 GMT"}
                })
            }
        })
        
        render(
            <MemoryRouter initialEntries={['/']}>
                <Timestamp />
            </MemoryRouter>
        );
        const timestamps = screen.getAllByRole("paragraph");
        expect(timestamps[0]).toHaveTextContent("Date in UNIX: 7728271946");
        expect(timestamps[1]).toHaveTextContent("Date in UTC: Tue, 31 Mar 1970 10:44:31 GMT");
    }
);
test(
    "Verify that DateError a displays a message that the supplied date is an invalid date", () => {
        vi.mock("react-router-dom", async() => {
            const module = await vi.importActual("react-router-dom");
            return {
                ...module,
                useRouteError: vi.fn(() => "282347223489234 is an invalid date!")
            }
        });
        render(
            <MemoryRouter initialEntries={["282347223489234"]}>
                <DateError />
            </MemoryRouter>
        );
        const message = screen.getByRole("paragraph");
        expect(message).toHaveTextContent("282347223489234 is an invalid date!")
    }
);
function Timestamp(props) {
    const date = useLoaderData();

    return <div className="timestamp_wrapper">
        <p className="timestamp">Date in UNIX: {date.unix}</p>
        <p className="timestamp">Date in UTC: {date.utc}</p>
    </div>
}

function DateError(props) {
    const error = useRouteError()

    return <div className="timestamp_wrapper">
        <p className="timestamp_error">{error}</p>
    </div>
}

main.jsx

const router = createBrowserRouter(
    createRoutesFromElements(
        <Route path="/" element={null}>
            <Route path=":date" element={<Timestamp />} errorElement={<DateError />} loader={get_timestamps}/>
        </Route>
    )
);

ReactDOM.createRoot(document.getElementById('root')).render(
    <RouterProvider router={router} />
)

I'm not sure if this error is a result of how my tests are written with Vitest or React Router given I'm still learning how to use both.

like image 208
0binny0 Avatar asked Feb 04 '26 15:02

0binny0


1 Answers

You appear to be using a regular, non-data, MemoryRouter component in the unit tests. If you are using the Data APIs then even the code in unit tests must still be rendered within a Data Router.

Example:

import {
  createMemoryRouter,
  createRoutesFromElements,
  RouterProvider,
} from 'react-router-dom';

test(
  "Verify that <Timestamp /> displays both the UNIX and UTC dates for a date provided in milliseconds",
  () => {
    vi.mock("react-router-dom", async () => {
      const module = await vi.importActual("react-router-dom");
      return {
        ...module,
        useLoaderData: vi.fn(() => {
          return { unix: 7728271946, utc: "Tue, 31 Mar 1970 10:44:31 GMT" }
        }),
      };
    });

    const router = createMemoryRouter(
      createRoutesFromElements(
        <Route path="/" element={<Timestamp />} />
      ),
      {
        initialEntries: ['/'],
      }
    );
        
    render (
      <RouterProvider router={router} />
    );

    const timestamps = screen.getAllByRole("paragraph");
    expect(timestamps[0]).toHaveTextContent("Date in UNIX: 7728271946");
    expect(timestamps[1]).toHaveTextContent("Date in UTC: Tue, 31 Mar 1970 10:44:31 GMT");
  }
);

My only other suggestion from here would be to use a mock loader function on the route instead of mocking the useLoaderData hook as this would be a bit more "organic" in my opinion.

Example:

import {
  createMemoryRouter,
  createRoutesFromElements,
  RouterProvider,
} from 'react-router-dom';

test(
  "Verify that <Timestamp /> displays both the UNIX and UTC dates for a date provided in milliseconds",
  () => {
    const loader = () => ({
      unix: 7728271946,
      utc: "Tue, 31 Mar 1970 10:44:31 GMT"
    });

    const router = createMemoryRouter(
      createRoutesFromElements(
        <Route path="/" loader={loader} element={<Timestamp />} />
      ),
      {
        initialEntries: ['/'],
      }
    );
        
    render (
      <RouterProvider router={router} />
    );

    const timestamps = screen.getAllByRole("paragraph");
    expect(timestamps[0]).toHaveTextContent("Date in UNIX: 7728271946");
    expect(timestamps[1]).toHaveTextContent("Date in UTC: Tue, 31 Mar 1970 10:44:31 GMT");
  }
);
like image 88
Drew Reese Avatar answered Feb 06 '26 04:02

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!