Exploring routing, data fetching and error handling in React Router 6.4+ (2024)

Esther Itolima

Posted on • Updated on

Exploring routing, data fetching and error handling in React Router 6.4+ (2) Exploring routing, data fetching and error handling in React Router 6.4+ (3) Exploring routing, data fetching and error handling in React Router 6.4+ (4) Exploring routing, data fetching and error handling in React Router 6.4+ (5) Exploring routing, data fetching and error handling in React Router 6.4+ (6)

#react #datafetching #errors #api

Around September 2022, the Remix team introduced React Router 6.4, packed with latest features and patterns that streamline various tasks within your React application.

These enhancements are designed to simplify behaviors like loading and updating data, ensuring automatic data revalidation after updates, handling navigation race conditions, managing errors effectively, creating pending and skeleton UIs, and more, all based on the Remix team's insights.

In this article, we'll mainly delve into the following aspects:

  • Page Navigation
  • Data Fetching
  • Loading Data into Components
  • Efficient Error Management and User-Centric Rendering

While the allure of trying out new features can be strong, it's advisable to balance innovation with what already works for you. Just like preferring new clothes over old ones. You should adopt features that suit your project's needs.

Before embracing these enhancements in your next project, take the time to comprehend how they function. This article serves as your guide in understanding and implementing these exciting features effectively. Let’s get started. 😊

Prerequisites

Before we continue, make sure you have your project setup in your local machine.

  • Basic understanding of React.
  • Install the new react application and run the application
  • Install the latest version of react-router-domnpm install react-router-dom

Page navigation

With React Router 6.4, navigating through your application's pages has become remarkably simpler, requiring fewer lines of code.

Page navigation, or routing, is an indispensable aspect of building React applications. It's a fundamental skill that enables you to showcase content dynamically based on specific routes, all without the need for page refresh. This not only leads to a smoother user experience but also optimizes your application's performance.

There are instances where you want to seamlessly display content as users explore different sections of your app, and React Router 6.4 equips you with the tools to achieve this efficiently.

i. Setting up Routes in React Router 6.4+

Prior to delving into route configuration, ensure that react-router-dom is present in your package.json file. If it's not already there, you can add it by executing the command npm install react-router-dom in your terminal. This step is essential as it provides the foundation for establishing and managing routes using React Router 6.4+. Once you have this dependency in place, you'll be ready to define and organize the navigation paths within your application effectively.

Exploring routing, data fetching and error handling in React Router 6.4+ (7)

Now, let's delve into router configuration.

To get started, we import createBrowserRouter from react-router-dom. This function plays a crucial role in directing the routing of our application within the updated router version. This crucial step sets the stage for defining how different routes will guide users through your application's various pages and components. With createBrowserRouter, you gain the power to establish clear paths for seamless navigation, making the most out of React Router 6.4's capabilities.

import { createBrowserRouter, createRoutesFromElements, RouterProvider, Route, Link, Outlet } from 'react-router-dom'

Inside your App.js component, declare a variable named router and assign it to createBrowserRouter function.

Configuring routes involves two distinct approaches, each offering its advantages:

  1. Array of Objects: One option is to define routes through an array of objects. Each object contains properties such as path and element. This clear configuration helps map out the paths and associated components for navigation.
const router = createBrowserRouter([ { path: '/', element: <Home />}, { path: '/about', element: <About /> } ])

  1. Using createRoutesFromElements: Alternatively, leverage createRoutesFromElements within createBrowserRouter. Here, you use route definitions as JSX elements. The primary route, functioning as a parent, shapes the layout that accommodates all subsequent child routes.
const router = createBrowserRouter(createRoutesFromElements( <Route path='/' element={<Root />} > <Route index element={<Home />} /> //default route <Route path='/about' element={<About />} /> </Route> ));

As you proceed, it's important to note that the Root component, if use as an element within a Route, might require creating it in a separate file or even within the same App.js file, depending on the project's structure and your preferences. This ensures a well-organized and efficient routing structure.

ii. Navigating between pages in a client-side application using React Router 6.4+

In the context of this article, we'll illustrate page navigation using two(2) components:

Home component and About component

//Content of the Home Componentconst Home = () => { return ( <div>This is the Home page</div> )}export default Home

//Content of the About Componentconst About = () => { return ( <div>This is the About page</div> )}export default About

Incorporate the links to our pages using the <Link> from react-router-dom, ensuring alignment with the corresponding paths defined in the router variable.

When utilizing the <Link>, the to attribute plays a crucial role. Essentially, clicking on the text "About" triggers the inclusion of /about in the page's URL. Subsequently, the designated component with the identical path from the router variable, in this instance <Route path='/about' element={<About />} />, gets rendered.

This seamless interaction enables users to effortlessly navigate between content while maintaining a coherent and engaging user experience.

//Root component or elementconst Root = () => { return ( <div> <header> <Link to="/">Home</Link> <Link to="/about">About</Link> </header> <main> <Outlet/> </main> </div> )}

A closer observation reveals that the parent Route within the router variable, housing the Root component, holds the path ‘/’. Consequently, when launching the application, the default route, which is Home, is displayed due to its position as the index route within the Root Route.

Within the primary element, the <Outlet/> takes center stage. Its purpose is to seamlessly render the content of the Route nested within the Root Route.

However, at this juncture, our root file - App.js, lacks direct access to the previously crafted router variable. To bridge this gap, the solution lies in utilizing the <RouterProvider/> from react-router-dom. By embedding router as a prop within <RouterProvider/>.

<div className='App'> <RouterProvider router={router} /></div>

Here is our final result working as intended.

Exploring routing, data fetching and error handling in React Router 6.4+ (8)

Data Fetching

In React Router 6.4+, data fetching assumes a crucial role, facilitating swift data retrieval each time a page is rendered. If you're keen to delve deeper, I've penned an article detailing data access within components, leveraging React Hooks.

Feel free to explore it: How to pass and access data from one route to another with useLocation, useNavigate, useHistory hooks.

For software developers, data fetching ranks high in importance when building applications. In essence, data fetching entails retrieving information from a server and seamlessly presenting it in response to user requests.

i. How to load data from API in React Router 6.4+

The latest React Router version has made fetching data from APIs incredibly simple, reducing the amount of code required.

This is achieved through the use of loader props, which streamline the process of fetching data from an API before the component is displayed in the browser. As a result, there's no longer a need to employ the useEffect hook to fetch data during component rendering. It's like achieving two goals with a single action.

Traditionally, when fetching data in a component, developers used the useEffect hook to fetch data and often utilized the useState hook to store it. In React Router 6.4+, this process is simplified further using the useLoaderData() hook. This means you don't have to rely on either the useEffect or useState hook to store API data.

In our illustration, we'll work with the free Pokemon API and create a dedicated page to showcase this streamlined data loading approach.

const Poke = () => { const result = useLoaderData() return ( <div> This is the Poke page { data.game_indices.map(itm => ( <div key={itm.version.name}> <p > {itm.version.name} </p> </div> )) } </div> )}export default Pokeexport async function pokeData(){ const response = await fetch('https://pokeapi.co/api/v2/pokemon/ditto') .then(response => response.json()) const resData = await response return resData;}

Examining the code above, we're using the fetch API to grab data from the Pokemon API. Importantly, getting data doesn't have to happen in the exact same place where we want to show it.

We can actually fetch data in a different component, then bring the function that fetches the data over and connect it to a specific pathway.

When we want to display the data we fetched using the pokeData function in the Poke component, we'll need to tap into something called "loader props". This is like a special tool that the Poke component gets. With this tool, we hand over the pokeData function, and it makes sure the data is fetched and ready to be shown when the Poke component shows up. It's a bit like preparing all the ingredients before cooking a meal. This way, things are nicely organized and work smoothly.

import './App.css'import { createBrowserRouter, createRoutesFromElements, RouterProvider, Route, Link, Outlet } from 'react-router-dom'import Home from './page/Home'import About from './page/About';import Poke from './page/Poke';import { pokeData } from './page/Poke' //imported data functionfunction App() { const router = createBrowserRouter(createRoutesFromElements( <Route path='/' element={<Root />} > <Route index element={<Home />} /> <Route path='/about' element={<About />} /> <Route path='/poke' element={<Poke />} loader={pokeData} /> </Route> )); return ( <div className='App'> <RouterProvider router={router} /> </div> )}

So whenever we visit /poke route, the pokeData function will run ahead of time, fetch the data, store the data response in the result by setting it to useLoaderData() hook from react-router-dom, and voila………..! you have access to render the data in that page.

Exploring routing, data fetching and error handling in React Router 6.4+ (9)

Error handling

Sometimes, when you go to a page, you might see a big "404 Not Found" message with some other explanations. This basically means that the page you're looking for isn't there or doesn't exist.

Exploring routing, data fetching and error handling in React Router 6.4+ (10)

You wouldn't like to present users with a strange-looking page like that when they're trying to reach the poke page, especially if the API data isn't working or no response is returned.

The good news is, React Router 6.4+ makes it simple for you to manage these situations. It provides ways to show a more attractive error page using tools like errorElement props and useRouteError()hooks.

i. How to handle and render errors with useRouteError hook

To begin, we'll adjust our fetch API function to enable us to see if the fetch was successful or not. You can find guidance on this at MDN: Checking that fetch was successful.

export async function pokeData() { try { const res = await fetch( "https://pokeapi.co/api/v2/pokemon/ditto" ); const resData = await res.json(); if (!res.ok) { throw Error('Name is not available!'); } return resData; } catch (error) { throw Error('Name is not available!'); }}

Create a separate page component for the Error Page, import useRouteError hooks from react-router-dom, and assign it to a variable which will be the error that you throw in pokeData function. Using the variable you can be able to access the error message on that page.

Create a new component for the Error Page, and bring in the useRouteError hooks from react-router-dom. Set it to a variable, which will hold the error you create in the pokeData function. This variable lets you get the error message and display it on that page.

import { Link, useRouteError } from "react-router-dom"const PokeError = () => { const error = useRouteError(); return ( <div> <p>{error.message}</p> <Link to='/'>Back to home page</Link> </div> )}export default PokeError

Pass the error using the errorElement props into the associated Route and simply give it the Error page as the value.

import './App.css'import { createBrowserRouter, createRoutesFromElements, RouterProvider, Route, Link, Outlet } from 'react-router-dom'import Home from './page/Home'import About from './page/About';import Poke from './page/Poke';import { pokeData } from './page/Poke'import PokeError from './page/PokeError';function App() { const router = createBrowserRouter(createRoutesFromElements( <Route path='/' element={<Root />}> <Route index element={<Home />} /> <Route path='/about' element={<About />} /> <Route path='/poke' element={<Poke />} loader={pokeData} errorElement = { <PokeError/>} /> </Route> )); return ( <div className='App'> <RouterProvider router={router} /> </div> )}

Note: You have two options: you can use the errorElement props in either the main parent Route or the associated Route.

If the errorElement props are set in the parent Route, and you attempt to go to an invalid route like /pokes, the Error page will show up as expected. However, the error message specific to the Poke page won't display, and you'll see the other content instead.

Exploring routing, data fetching and error handling in React Router 6.4+ (11)

Exploring routing, data fetching and error handling in React Router 6.4+ (12)

In conclusion, React Router 6.4+ brings exciting features that make building React applications easier. It simplifies tasks like loading and updating data, managing errors, and improving the user interface.

Navigating between pages is now simpler and faster with the new routing setup. Data fetching, which is crucial, is made easier with the useLoaderData() hook. It loads data before the page is rendered, resulting in quicker loading times. Handling errors is also improved, making error messages more user-friendly.

React Router 6.4+ provides valuable tools that enhance navigation, data management, and error handling, making React development more efficient and user-focused.

As a seasoned expert in React and web development, I have a deep understanding of the concepts discussed in the article. My expertise is grounded in practical experience and a comprehensive knowledge of React, React Router, data fetching, error management, and user-centric rendering. Let's delve into the key concepts covered in the article:

  1. React Router 6.4:

    • The article introduces React Router 6.4 as an enhanced version with new features and patterns for React applications.
    • It emphasizes the importance of page navigation, data fetching, and error management in building efficient React applications.
  2. Page Navigation:

    • Page navigation, facilitated by React Router 6.4, is crucial for dynamic content presentation without page refresh.
    • The article demonstrates setting up routes using the createBrowserRouter function and configuring routes through an array of objects or JSX elements.
  3. Data Fetching:

    • React Router 6.4+ simplifies data fetching using loader props, eliminating the need for complex code structures.
    • The example uses the free Pokemon API to illustrate loading data into components using the useLoaderData() hook.
  4. Loading Data into Components:

    • The article provides a clear example of loading data from an API using the useLoaderData() hook, which replaces the traditional use of useEffect and useState.
    • The loader function (pokeData) fetches data from the Pokemon API, and the data is seamlessly passed to the component using loader props.
  5. Efficient Error Management:

    • React Router 6.4+ introduces tools for efficient error handling, ensuring a better user experience.
    • The article demonstrates how to handle and render errors using the useRouteError hook and the errorElement prop.
  6. User-Centric Rendering:

    • The article emphasizes the importance of user-centric rendering in React applications.
    • Error pages are customized to provide a more attractive and user-friendly experience when there are issues with data fetching or invalid routes.
  7. Prerequisites:

    • The article outlines prerequisites for working with React Router 6.4, including having a basic understanding of React and installing the necessary dependencies.
  8. Implementation:

    • The article provides practical implementation steps, including setting up routes, navigating between pages, loading data, and handling errors.

In conclusion, the article serves as a comprehensive guide for developers looking to leverage the new features in React Router 6.4 for efficient page navigation, data fetching, and error management in React applications. The practical examples and in-depth explanations make it a valuable resource for both beginners and experienced React developers.

Exploring routing, data fetching and error handling in React Router 6.4+ (2024)
Top Articles
Latest Posts
Article information

Author: Lilliana Bartoletti

Last Updated:

Views: 5851

Rating: 4.2 / 5 (73 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Lilliana Bartoletti

Birthday: 1999-11-18

Address: 58866 Tricia Spurs, North Melvinberg, HI 91346-3774

Phone: +50616620367928

Job: Real-Estate Liaison

Hobby: Graffiti, Astronomy, Handball, Magic, Origami, Fashion, Foreign language learning

Introduction: My name is Lilliana Bartoletti, I am a adventurous, pleasant, shiny, beautiful, handsome, zealous, tasty person who loves writing and wants to share my knowledge and understanding with you.