Updating React Router v5 to v6

TiShow
3 min readDec 25, 2021

I’ve tried to update the React Router from v5 to v6, so I’ve reviewed and summarized what I’ve done. We are conscious of updating with minimal code fixes to make it easier to review.

Please refer to the update procedure on the official website first.

What I’ve worked to day is as follows.

Supports changing the type of useParams
Supports changing the type of <NavLink />
Change <Switch /> to <Routes />
Supports <Route /> type changes
Change <Redirect /> to <Navigate />
Change useHistory to useNavigate
Change useRouteMatch to useMatch

Let’s get started.

Supports changing the type of useParams

When getting the path parameter of the path such as / user /: userId / content /: contentId, v5 itself is like below

type ParhParams = {
userId: string;
contentId: string;
};
const { userId, contentId } = useParams<Params>();

v6 is now defined as follow

export declare function useParams<Key extends string = string>(): Readonly<
Params<Key>
>;
export declare type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
// type of userId and contentId is `string | undefined`
const { userId, contentId } = useParams<”userId” | “contentId”>();

Therefore, it is necessary to deal with the case of ‘undefined’. It’s okay to do something like userId ?? “”, but I used Assertion Functions to handle it.

function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
if (val === undefined || val === null) {
throw new AssertionError(
`Expected ‘val’ to be defined, but received ${val}`
);
}
}

Supports changing the type of <NavLink />

A simple rename is required from exact of props to end.

The deprecation of activeClassName and activeStyle is likely to require major modifications in some cases. The project to be replaced uses Material UI v5 and could not be described as follows:

import { Link } from “@mui/material”;
import { NavLink } from “react-router-dom”;

<Link
className={({ isActive }) => (isActive ? “isActive” : “”)}
component={NavLink}
to=”/”
>
{children}
</Link>;

There were only a few problematic points, I deal with it as follows.

<Box sx={{ “> .isActive”: { color: “white” } }}>
<NavLink className={({ isActive }) => (isActive ? “isActive” : “”)} {…rest}>
{children}
</NavLink>
</Box>

It seems good to have a wrapper as explained officially.

Change <Switch /> to <Routes />

It was just a simple replacement because the project being updated did not nest the Switch.

Supports <Route /> type changes

Deleted exact since it is no longer needed.

Changing component to element was a simple task, but if you are using Auth0 for authentication like below

const ProtectedRoute = (props: RouteProps) => {
const { component, …rest } = props;
return (
<Route
component={withAuthenticationRequired(component as React.ComponentType)}
{…rest}
/>
);
};

Since element = {withAuthenticationRequired (component)} cannot be set, the following measures you should take

const Protected = withAuthenticationRequired (Outlet);

Change <Redirect /> to <Navigate />

In v5, push requires the specification of props, but in v6, the default is push, and replace requires the specification of replace.

// v5
<Redirect to=”/foo” />
<Redirect to=”/bar” push />
// v6
<Navigate to=”/foo” replace />
<Navigate to=”/bar” />

Change useHistory to useNavigate

// v5
const history = useHistory();
history.push(“/”);
history.replace(“/”);
history.goBack();
// v6
const navigate = useNavigate();
navigate(“/”);
navigate(“/”, { replace: true });
navigate(-1);

As with <Navigate />, if the default is push or replace, you need to specify it. In the case of go, goBack, goForward, give number as an argument.

Change useRouteMatch to useMatch

From v5 to v6, the way to get the Path Pattern of the current URL (like / user /: userId) from the component side has changed.

In v5, you could simply use useRouteMatch, but useMatch needs to pass the Path Pattern you want to check in order to get the Path Pattern.

// v5
const match = useRouteMatch ();
// v6
const match = useMatch (“/user /:userId”);

Therefore, you cannot use useMatch if the condition has multiple Path Patterns.

For example, if you want to do something with either the / foo or / bar URL, you should use matchPath instead of useMatch.

const { pathname } = useLocation();
const match = useMemo(() => {
return [“/foo”, “/bar”].find((path) => !!matchPath(path, pathname));
}, [pathname]);

Summary

I think it would be more reviewer friendly to focus on the minimal code fixes for PR of team-developed package updates, and this article also focused on that.

Java Scriptmas !!

--

--

TiShow

80% is the creation, the rest is depression. Developer and Data scientist. Looking for Physics Ph.D Twitter: @_t_i_show