The react-router-dom-v5-compat package enables React Router web apps to incrementally migrate to the latest API in v6 by running it in parallel with v5. It is a copy of v6 with an extra couple of components to keep the two in sync
Incremental Migration
Instead of upgrading and updating all of our code at once (which is incredibly difficult and may cause bugs), we planned to release the react-router migration in two phases...
It looks something like this:
- Setup the
CompatRouter
- Change a
<Route>
inside of a<Switch>
to a<CompatRoute>
- Update all APIs inside this route element tree to v6 APIs one at a time
- Repeat for all routes in the
<Switch>
- Convert the
<Switch>
to a<Routes>
- Repeat for all ancestor
<Switch>
s - Update
<Links>
You're done!
Phase 1: π
npm install react-router-dom-v5-compat
Install a Compatibility Package that is react-router-dom-v5-compat - This package includes the v6 API so that you can run it in parallel with v5. (This package helps migrate our code stages instead of making a big, and risky upgrade in our app )
import { BrowserRouter } from "react-router-dom";
import { CompatRouter } from "react-router-dom-v5-compat";
export function App() {
return (
<BrowserRouter>
<CompatRouter>
<Switch>
<Route path="/" exact component={Home} />
{/* ... */}
</Switch>
</CompatRouter>
</BrowserRouter>
);
}
CompatRouter that synchronizes the v5 and v6 APIs state so that both APIs are available.
Done π₯... We ready for migration
Phase 2:πΊοΈ
Convert <Route>
to <CompatRoute>
import { Route } from "react-router-dom";
import { CompatRoute } from "react-router-dom-v5-compat";
export function SomComp() {
return (
<Switch>
<Route path="/project/:id" component={Project} />
<CompatRoute path="/project/:id" component={Project} />
</Switch>
)
}
<CompatRoute>
renders a v5 <Route>
wrapped inside of a v6 context. This is the special sauce that makes both APIs available to the component tree inside of this route.
then Migrate below React Router APIs
Upgrade all elements to :
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/products">
<Products />
</Route>
</Switch>
</BrowserRouter>
)}
function Products() {
let match = useRouteMatch();
return (
<div>
<Link to={`${match.url}/me`}>My Profile</Link>
<Switch>
<Route path={`${match.path}/me`}>
<Own />
</Route>
<Route path={`${match.path}/:id`}>
<UserProfile />
</Route>
</Switch>
</div>
);
}
to (v6) :
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users/*" element={<Products />} />
</Routes>
</BrowserRouter>
)}
function Products() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Routes>
<Route path=":id" element={<Product />} />
<Route path="me" element={<OwnUserProfile />} />
</Routes>
</div>
);
}
We donβt use Component as a child anymore instead we will use Component in the element props
### what are the changes? β
* change all the `<Switch>` to `<Routes>`
* All `<Route>`s and `<Link>`s inside a `<Routes>` are relative. This leads to leaner and more predictable code in `<Route path>` and `<Link to>`
* `<Route exact>` is gone. Instead, `<Route>` automatically finds the exact matches, and use a * in their end of the path to indicate they match deeply
Valid route paths:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
Invalid route paths:
/users/:id?
/tweets/:id(\d+)
/files//cat.jpg
/files-
π Read from v6 useParams() instead of v5 props.match
+ import { useParams } from "react-router-dom-v5-compat";
function Project(props) {
- const { params } = props.match;
+ const params = useParams();
// ...
}
Use βuseNavigate()β instead of βuseHistory()β:
From (v5) :
import { useHistory } from "react-router-dom";
function App() {
let history = useHistory();
function gotoHome() {
history.push("/home");
}
function backToLogin() {
history.replace("/login");
}
return (
<div>
<button onClick={gotoHome}>go to Home</button
<button onClick={backToLogin}>login</button>
</div>
);
}
to (v6) :
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function gotoHome() {
navigate("/home");
}
function backToLogin() {
navigate("/login", { replace: true });
}
return (
<div>
<button onClick={gotoHome}>go to Home</button
<button onClick={backToLogin}>login</button>
</div>
);
}
Use <Navigate>
instead of <Redirect>
:
From (v5) :
<Redirect to="about" />
<Redirect to="home" push />
to (v6) :
<Navigate to="about" replace />
<Navigate to="home" />
Replace useRouteMatch
with useMatch
:
useMatch
is very similar to v5's useRouteMatch
NavLink ActiveClassName removed:
<NavLink
to="/messages"
className="nav-link"
activeClassName="activated"
className={({ isActive }) => "nav-link" + (isActive ? " activated" : "")}
>
Messages
</NavLink>
Remove <Prompt>
π:
<Prompt>
from v5 is not currently suppported in v6
π Remove the compatibility package
npm uninstall react-router-dom-v5-compat
π Uninstall react-router and history
npm uninstall react-router history
π Install React Router v6
npm install react-router-dom@6
Phase 3: π
Once we migrated all of our code we can remove all the <CompatRoute>
, <CompatRouter>
and compatibility package (react-router-dom-v5-compat)
and install React Router DOM v6 directly.
import { BrowserRouter } from "react-router-dom";
export function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
{/* ... */}
</Routes>
</BrowserRouter>
);
}
Finally, we fully migrated to React Router V6 π π
Don't forget to comment down if you're stuck, add your own tips, and help others with their questions π
Top comments (0)