React Router 6 - What Changed & Upgrading Guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
- React Router version 6 was released and this is quite important because React Router is one of the most used and most important React packages that you find out there. A lot of React projects need routing and therefore, a lot of React projects do use React Router. In this video, I will walk you through what's new with React Router version 6 and of course, I will also show you how you could update an existing React app that's using React Router version 5 to React Router version 6. Now, to learn about React Router version 6, you can, of course, check out the official website, ReactRouter.com, and the documentation you find there. And specifically, there also is a upgrading guide, which I will attach to this video where you will find detailed upgrading steps and where you also learn what's new and what changed. And this looks like a super long document, and if you wanna have all the details, you should definitely also dive into it. But in the end, it's really simple to upgrade, and not a lot changed when it comes to the code that we write. Under the hood, version 6 is a lot better than version 5, and therefore, if you can upgrade, you should, of course, strongly consider doing that. Now, to see what changed and write some code, I created a little snapshot, a little project snapshot, which you find attached, which does use React Router version 5. So which does not use version 6. So once you downloaded and extracted this snapshot, you should run npm install to install all the core dependencies that come with that project. And once you did that, you should install React Router version 6. And you do this by running npm install react-router-dom, and that's important. You want react-router-dom, which is the browser version of React Router. And then add @6, which ensures that you install the latest version 6. Or you add @latest, which will always give you the very latest version, and that's what we can do here. So with that installed, we got React Router version 6. And if I now run npm start, you will see that this project won't work. If I try to visit this page, I now get an error that Switch is not exported from react-router-dom. And that's one of the first changes. When using React Router version 5, we used this Switch component, which is provided by the react-router package to wrap our routes and make sure that only one of these routes is loaded at the same time, instead of all matching routes. And typically, that is what you want. You want to define multiple routes but only one route should be active at a given point of time. Now, with React Router version 6, Switch doesn't exist anymore. Instead, this now becomes Routes. So you replace Switch with Routes. And you therefore import Routes from react-router-dom instead of Switch. By the way, what hasn't changed is that you still import BrowserRouter from react-router-dom, and you wrap that around your app where you then plan to use routing. That's exactly the same syntax as you know it. With that, the error goes away. But if I click Welcome or Products, you see that the URL changes but I still don't see anything on the screen. The reason for this is that the way how we define our routes also changed. We still have the route component and it still takes a path prop, but the component that should be loaded when a given path becomes active, so in this case, the welcome component, for example, is not longer a child of your route but instead, on Route, you add a new element prop, and then you pass a dynamic value to element and that dynamic value is that to be rendered component as JSX. That's important. So you don't pass Welcome like this, you don't pass a pointer to that component function. Instead, you pass the JSX element. Hence the name element. It wants a JSX element. And then this can, of course, become a self-closing component, this route component. And we do the same here for products. Here we also add the element prop, and we then move Products into this prop or as a value, we set it as a value for this prop. And we turn this into a self-closing component. And last but not least, we also do this for this last route. And please note that this is a dynamic route and that still works as you learned it. So you can still have dynamic path parameters, like productId as you could with version five. That did not change. With that, once the routes were restructured like this, now you see I see this welcome page content here, and I can switch to products back and forth. If I select a product, I also do see those product details. So those detail routes also works and therefore, a simple app using routes like this can easily be updated to version 6 by replacing Switch with Routes, and moving the two below that component into this new element prop. But that's not all you should know about version 6. In addition to these changes, it's also worth noting that there were some internal changes with React Router version 6. Specifically, the internal logic for evaluating these paths and then picking a route to load changed. With React Router version 5, we needed to add exact here because without exact, this would match if a path started with /products. With version 6, this behavior is gone and hence, the exact prop is gone. It now always looks for exact matches if you define your paths like this. So we can remove exact with version 6, and we still get the same behavior as before. If you compare that to the version 5 behavior where without exact you would load the products component and the product detail component if you pick a detail. That, of course, is an improvement because you don't need that extra prop anymore. If you want that old behavior of matching the start of a path only, you can still get that by adding /* after your path. Once you add that, this route will become active if a URL path starts with /products instead of being only /products. However, what you will note is that even if you do that, and you go back to your site here, if you pick a product detail, you still get the product detail instead of the products overview page, even though I'm now looking for the start of the path. The reason for that is that internally, React Router version 6 now also has a better algorithm for picking the best route to be loaded for a given path. And what it basically does here is it sees that if your path is /products/ some other value, that the best fit for this kind of path probably is this route because you explicitly declared this route here where you say that for /products/ something, you want ProductDetail. So unlike with version 5, the order of routes also doesn't matter anymore. With version 5, you had to be careful. For example, if you also had another route that should be /products/edit. Then with version 5, this kind of route here would have to be defined before you have the route with the dynamic segment because if you had it thereafter, this route would always win. With version 6, this changed and it's smarter internally. You could define these two routes, and if you then visit /edit here, so products/edit, React Router would find out that this last route here is the best match for this specific path. And if you had /products/p1, on the other hand, it would determine that you have no better match than this route, and hence, this route would become active. So the order of routes doesn't matter anymore when working with React Router version 6. And that's another important thing to note. I'll also revert this here because it didn't have any effect here anyways. So these are a couple of super important changes but as you see, code wise, there wasn't a lot to change. Now, let's move from route definition to links. The first important thing to note is that you still have the Link component, which generally works as you learned it and you also still have the NavLink component. That did not change. However, what did change is this activeClassName prop. With React Router version 5, you could use that prop to apply some CSS class automatically to the link once it became active. With React Router version 5, this prop was removed. Instead, if you want to apply a specific class once this is active, you have to manually find out whether this link is active or not. Doing that is very simple though because you can use the className prop or the style prop to apply dynamic styling. And both props work in a special way when applied to NavLink. className does not just take a class name, instead, it takes a function, which you can pass here, and this function will actually give you some information about this link and the current state of navigation. So get some NavData here you could say. This argument is provided by React Router to this function when this function is executed. And this function will be executed by React Router when it evaluates NavLink and whenever your navigation changes. Inside this navData object, which you get by React Router, you then find a isActive property. And that will be true if this link is active for the currently chosen path or not. So you can then use this to return a className dynamically that should be applied to NavLink. So here, I can get rid of those curly braces to use this short form of this arrow function syntax. I could then check if navData.isActive is truthy. In which case, I can apply my active class here and otherwise, I apply no class. That's what I could do here. And I can do the same here for the other NavLink. It's a bit more verbose but it also gives you more customization possibilities and more control and therefore, it is a change that's not too difficult to incorporate, and you then still get the old behavior as you can now see in the navigation here. So that's an important thing you should know about links. I also wanna have a brief look at the ProductDetail component here. The ProductDetail page to be precise. There I'm getting this dynamic parameter, which is part of the URL. And with React Router version 5, we extracted that with the useParams hook that was provided by the react-router-dom package. As you can tell, this code still seems to work because we had no problems extracting that dynamic parameter with React Router version 6 as well. So the code here stays the same. Now, let's move on to a slightly more advanced app here, In this snapshot, we also have a redirect, and we have a nested route in the Welcome.js file. Now, here we also got some noteworthy changes that come with React Router version 6. And let's start with redirect because that's already causing an error here because the redirect component doesn't exist anymore. Now, you can still redirect, of course. The only thing that changed is that now instead of redirect, it's Navigate. That's a new component provided by React Router 6, and we can use this here to well, navigate. And, of course, we then also should use this element prop instead of passing it as a child between the opening and closing route tags. Though to be precise, if we just add it like this, what we will actually do is we will push a navigation to this page onto the navigation stack. If we truly want to redirect, so replace the current page with the new page, then we would have to add the replace prop to Navigate as well. And this would then be the full replacement for the old redirect component. But you could also just configure it like this to just push the new page, instead of replacing the current page. With that, this error goes away. And as you see, if I now enter just slash, I am redirected to welcome. Now I get another error though. And that other error is not related to redirecting because that works with Navigate as you saw it here. Instead this error is now related to this nested route, which we have here inside another component. Nested routes can be useful because they allow you to build deeply nested page structures but the syntax you know from version 5 from React Router version 5 doesn't work like this anymore. However, updating it is also simple. The key thing you have to do here, which you also learn in this error message is that you need to wrap your Route with Routes. In version 5, you didn't need to do that. You didn't have to wrap your Route definition components with the Routes or Switch component. With version 6, this is mandatory though. Hence, we should import Routes here as well. And wrap this single Route with Routes. So even if it's just one route, you have to wrap it. That's just something you have to do. Now then, we, of course, also have to update this syntax to use the element prop, and pass our to be displayed JSX code to element instead of having it as a child. And with this update done, if we now save this, this works, we don't get the error anymore. But it also doesn't really work if you take a closer look. We see the welcome page, which is this title but we don't see the content here, Welcome, new user! Well, the reason for this is that I haven't loaded new-user yet. If I do change my path to /welcome/new-user, then it also doesn't work. Now, what's wrong here? Well, what's wrong is that the logic and the syntax you need for nested routes also changed, and it actually changed to the better. If you plan on using nested routes, you, first of all, have to start at the parent route that will eventually hold more nested routes. In this case, the Welcome route here. I mentioned before that you can add this /* thing if React Router should match this route for the start of the path instead of the full path. At the moment, I don't have this, I just have /welcome. And therefore, because of how React Router version 6 works, this will always match and load this welcome component if the entire path is exactly /welcome. Now, here it's /welcome/new-user though, and therefore, this route isn't loaded. So to load it, we could change it to /new-user but that would not be the main idea here. We want to keep /welcome and only load this title if we visit just /welcome but then we want to be able to, in addition, load this content here, this paragraph if we have the /new-user part after welcome. To make this possible, we have to embrace this new syntax of adding /* here. Now the welcome component will be loaded whenever our path starts with /welcome instead of having an exact full match. So therefore with this, you now see if I visit /welcome/new-user, we have our title back. But we still don't see that paragraph in here. That's happening because of another change introduced by React Router version 6. When you're working with nested routes, so with this kind of matching and then routes defined inside of the loaded component, as we have it here, then the paths of these nested routes actually are relative to the path of the parent route. So that means that this route here only becomes active if our path starts with /welcome. Therefore, the path here already assumes that we have /welcome at the beginning. Hence here, we only have to add the extra part, the extra elements in the path that are relevant for this route. We don't need to repeat /welcome/ anymore, we can just have new-user here. And that then tells React Router that this should become active if this component is active, which will be the case for /welcome/ something. And then this paragraph should be shown if we have /welcome/new-user because this is relative. Hence now if I save this, now we have our paragraph here for /welcome/new-user. If I just had /welcome, we would only have the title because of this relative matching. And therefore, that's also not too difficult to update and it's actually a good change because we can keep our nested route paths shorter, and we don't need to repeat the entire full path all the time. Instead, we can focus on the path elements that do differ and that are relevant for identifying this route. As a side note, if you had a link in here, then the to prop here would also be relative. So in the past with React Router version 5, you would have to write welcome/new-user here if you wanted to load this route and then here you would write your link like this with version 5. With version 6, since this is a link inside a component that already is loaded through a route, through this route here, this changes and that's relative as well. So links and routes are relative if they are loaded inside of a component that is already loaded because of another route. So if you're in a nested route, so to say. So therefore, that's how you could create such a link and hence, new-user, this link works as you would expect it to work. That's also something worth noting. Now, with all the changes I covered up to this point, I already did cover pretty much all important changes you should be aware of. And you hopefully see that upgrading really isn't too difficult. There is another important change and pattern that I wanna explore together with you though. We can define nested routes as we do it here, and there's nothing wrong with it but you could also write this here differently. Instead of defining your routes in the component where you want to load that nested route content, you could also go for a different approach. You could cut this route here from Welcome.js, and remove this Routes wrapper and remove these imports therefore, and go to your main route definition file, the App.js file with the App component in this case. And then you could also define nested routes right here in one and the same place. So here for the Welcome component where we wanna have a nested route, we can now actually use opening and closing route component tags, and put our nested routes between those opening and closing tags as children. And that's also the reason why we have this new element prop where we point at the to below that component because the children of the route component, if we have any, are no longer the to below that components but instead, possible nested routes. And, of course, not every route will have a nested route. Here in this example, most routes don't have a nested route but if you do have a nested route, as we have it here, then the advantage of defining it like this is that you have all your route definitions in one place. And it can simply be easier to see all supported routes and to work on this application if you have all your route definitions in one place instead of spreading them across multiple components as you did have to do it with React Router version 5. That's where you could also define your nested route like this here. And what I explained about the path and so on still applies. We still have path=new-user here because it's nested inside of this welcome route and hence, this will actually be /welcome/new-user. Now, if you use this pattern though, you have to tell React Router where this nested content should be inserted into the DOM because it was clear when we had the route definition in the nested component, now that we have the route definition in App.js, it's not clear where exactly in Welcome.js this paragraph should be added. Should it be added here? Should it be added here after the link? How would React Router know? To let React Router know where that nested content should be inserted, there is another new component, which you can import from react-router-dom. And that's the Outlet component. You can add it like this, and this simply is a placeholder telling React Router where nested content, nested route content should be inserted. So we can add this here in Welcome. And with our nested route definition here, this, therefore, will still work as it did before. You see I can visit the welcome page with just /welcome, and I don't see the nested paragraph. But if I click this link, I do see it. And this is an alternative to defining nested routes inside of other components. You could still do that, as you saw it a couple of minutes ago but you can also go for this Outlet approach with all your routes defined in one central place, which might make your life a little bit easier. It's up to you which approach you prefer. Now, to conclude these updating instructions, there are two last notes I wanna make. The first note is about imperative navigation, instead of having links with which you navigate. Sometimes you want to navigate when a certain action finishes, when a button was clicked, when an HTTP request was sent. And with React Router version 5, you could navigate by using the useHistory hook, and you could then execute this hook in your component, and that would give you a history object. And then on this history object, you could push a new route or replace a route to redirect. That's the version 5 syntax. With version 6, useHistory doesn't exist anymore. Instead now, we have a useNavigate hook. This gives us a navigate object and function if we execute useNavigate, and this navigate function can be executed to navigate somewhere else. For example, if we would want to programmatically navigate to /welcome, this is how we could do it. And, of course, we would not do that right in a component like this typically. Instead, we would do that in a useEffect hook or when an HTTP request finished or anywhere else. But that's how you could navigate programmatically. If you want to redirect, you can pass a second argument, which is an object with some options and there you could set replace to true, which will replace the current route with the new one, which will redirect instead of pushing the new route onto the navigation stack. Now, besides passing a path here to navigate, you can also pass in numbers like -1 to, for example, go back to the previous page or -2 to go to the page before the previous page. Or one to go forward again. So forward and backward navigation is also possible with this navigate function. You simply pass numbers to it that tell React Router by how many pages you wanna go forward or backward. So that's programmatic or imperative navigation with React Router version 6. Now, as a last note, I wanna talk about the prompt component, which is provided by React Router version 5. You could use this component to prevent accidentally leaving a page if you have unsaved changes, for example. In React Router version 6, at least at this point of time when React Router version 6 was released, this component does not exist. So if you wanna prevent navigation in case of unsaved changes, you have to implement your own workaround for the moment. So for this reason, you might wanna stick to version 5 if this is a super important feature of your application, and you're not willing to come up with your own logic on how you could prevent navigation. This component will probably come back in the future as it sounds but at the moment, it's not part of React Router. And that's just something to note. But with that, that's it for this upgrade video or these upgrade instructions and I hope this was helpful for understanding what changed, and what this means for you, and how you can update your project. Attached, you also find a more complex project updated to React Router version six, And that should then also help you explore and practice how updating works and how React Router version 6 works.
Info
Channel: Academind
Views: 95,013
Rating: undefined out of 5
Keywords: react, reactjs, react router, react router 6, react router 6 upgrade, react router 6 update, react.js, react tutorial, reactjs tutorial, reactjs course, react router course, react router tutorial, tutorial, course, maximilian schwarzmuller, maximilian schwarzmueller
Id: zEQiNFAwDGo
Channel Id: undefined
Length: 29min 38sec (1778 seconds)
Published: Thu Nov 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.