How to Update MemoryRouter in React

React Router’s MemoryRouter is a routing component that stores its locations in memory rather than the browser’s address bar, making it ideal for testing and non-browser environments like React Native. This article explains how to update and navigate within a MemoryRouter programmatically using the useNavigate hook, configure its initial state using props, and trigger routing updates during component testing.

Programmatic Navigation with useNavigate

The standard way to update the active route inside a MemoryRouter is by using the useNavigate hook. Since MemoryRouter behaves like a standard router internally, any child component can trigger navigation updates.

Here is a basic example of how to update the route programmatically:

import { MemoryRouter, Routes, Route, useNavigate } from 'react-router-dom';

function NavigationTrigger() {
  const navigate = useNavigate();

  return (
    <button onClick={() => navigate('/dashboard')}>
      Go to Dashboard
    </button>
  );
}

export default function App() {
  return (
    <MemoryRouter initialEntries={['/']}>
      <Routes>
        <Route path="/" element={<NavigationTrigger />} />
        <Route path="/dashboard" element={<h1>Dashboard Page</h1>} />
      </Routes>
    </MemoryRouter>
  );
}

When the button is clicked, the useNavigate hook updates the internal history stack of the MemoryRouter, changing the rendered component to the /dashboard route.

Updating the Initial State with Props

To dynamically update or set the starting location of the MemoryRouter (such as in unit tests or when mounting a component), use the initialEntries and initialIndex props.

<MemoryRouter initialEntries={['/home', '/profile', '/settings']} initialIndex={2}>
  <App />
</MemoryRouter>

In this setup, the router starts at /settings (index 2). If you programmatically trigger a back action, the router will navigate to /profile.

Updating MemoryRouter in Unit Tests

When testing React components with libraries like React Testing Library, you often need to verify that a routing update occurred. Because MemoryRouter does not change the browser’s URL bar, you must assert against changes in the rendered DOM.

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';

test('navigates to dashboard on button click', async () => {
  render(<App />);
  
  // Verify initial route content
  expect(screen.getByText(/Go to Dashboard/i)).toBeInTheDocument();

  // Simulate user clicking the navigation button
  await userEvent.click(screen.getByRole('button', { name: /Go to Dashboard/i }));

  // Verify the MemoryRouter updated and rendered the new route
  expect(screen.getByText(/Dashboard Page/i)).toBeInTheDocument();
});

By interacting with the UI elements, you trigger the internal state updates of the MemoryRouter, allowing you to assert that your application navigates correctly.