Welcome to our React Router exercises! In this set of exercises, you'll learn how to build a basic product management system using React Router. You'll start by setting up the routing system in the main app component, then move on to constructing shared layout components, protected routes, and child components like product details and add/edit forms. Along the way, you'll also learn how to integrate authentication and handle undefined routes. Finally, you'll enhance several existing components with features related to React Router. These exercises aim to provide hands-on experience with React Router, helping you understand how it works and apply these concepts in practical scenarios. Let's get started!
Objective: This section is dedicated to ensuring that all the functionalities you've implemented throughout the exercise are working correctly. Periodically evaluating and testing your work is crucial in a real-world development setting, helping you to catch and address issues before they become bigger problems.
-
Checking Console for Errors:
- Always ensure there are no errors in the browser console. This is often the first indicator of any issues in your code.
-
Navigation and Route Validation:
- Navigate through every route you've set up in the application. Make sure that each route loads the expected component and handles edge cases (like undefined routes) appropriately.
-
SharedLayout Consistency:
- As you navigate through the application, verify that the shared layout elements like the header, navigation, and footer remain consistent across different pages.
-
Protected Routes:
- Try accessing protected routes without authentication by passing the user prop ass null. You should be redirected to the home page, signaling unauthenticated users are appropriately blocked.
- After authentication, ensure you can access these routes without any issues.
-
Functionality of the
Product
,AddProduct
, andEditProduct
Components:- For the
Product
component, check that it displays the right details for each product ID. - When adding a product using the
AddProduct
component, ensure that the product gets added and that you're navigated to the desired page afterward. - The
EditProduct
component should accurately fetch and display product details based on the URL's product ID. After editing, ensure the changes are saved and that navigation occurs as expected.
- For the
Remember, the objective is not just about building features but ensuring they work seamlessly and efficiently. Regular evaluation helps maintain the quality and reliability of your application. In the real world, these evaluations often lead to iterations where improvements and bug fixes are made.
Objective: The goal of this section is to guide you through the process of building the primary App
component for the web application. This component will establish the necessary routing system to navigate through the various parts of the application.
-
Defining the Routing System:
- Begin by defining the
routes
array. This array will store objects that dictate how navigation within your application operates. - Each route object should define a
path
that represents a URL segment and anelement
which specifies what component should be rendered when that path is accessed. - You can nest routes by including a
children
property in a route object. Nested routes are useful when a part of your application has a shared layout across multiple sub-pages.
- Begin by defining the
-
Setting up the Base Route:
- Create the first object in the
routes
array. This object should represent the root path,'/'
. - For this route, use the
SharedLayout
component as theelement
. Pass theuser
data to it as a prop. - As this route has several child routes (like Home, AddProduct, etc.), you will need to set up a
children
property that is an array of these child route objects.
- Create the first object in the
-
Configuring Child Routes:
- Set up the
index
route that represents the default child of the root path. This route should render theHome
component and receives theproducts
data as props. - Create another child route for adding a product. This path should be 'add' and it should use the
ProtectedRoute
component wrapping theAddProduct
component. Pass theuser
prop toProtectedRoute
. - Next, configure a nested route for products. It should be an object and the path should be 'products' and it should further have child routes for specific product details and editing a product. Use the dynamic
:productId
in the path to cater for any product ID.- For viewing a specific product, render the
Product
component with access to theproducts
data. - For editing a product, use the
ProtectedRoute
wrapping theEditProduct
component. Again, ensure theProtectedRoute
receives theuser
prop.
- For viewing a specific product, render the
- Set up the
-
Handling Undefined Routes:
- To ensure user-friendly behavior, set up routes to handle undefined or erroneous paths:
- As a catch-all for any undefined paths, add a route with the path
'*'
and have it render theNotFound
component.
- As a catch-all for any undefined paths, add a route with the path
- To ensure user-friendly behavior, set up routes to handle undefined or erroneous paths:
-
Implementing the Router:
- Now that your
routes
array is defined, create a browser router using thecreateBrowserRouter
function and passing theroutes
array to it. - Return the
RouterProvider
component. This provider requires arouter
prop which should receive the browser router you just created.
const App = () => { const router = createBrowserRouter(routes); return ( <RouterProvider router={router} /> ); };
- Now that your
- The
ProtectedRoute
component is specially designed to restrict access to certain routes based on user authentication. It's essential to wrap components likeAddProduct
andEditProduct
in this component to prevent unauthorized access. - Using dynamic segments like
:productId
in your paths enables React Router to extract parameters from the URL, which can be later used to fetch specific data or perform certain actions.
Objective: In this section, you will create the SharedLayout
component. This component will act as a shell for other components, ensuring consistent elements such as the header, navigation, and footer are present across multiple pages of your web application.
-
Initializing the Component:
- The necessary imports are given:
Outlet
, andNavLink
. - Next, define the
SharedLayout
functional component that takesuser
as a prop.
- The necessary imports are given:
-
Structuring the Layout:
- Inside the component's return statement, commence by embedding a
div
element with a class ofcontainer
. This div will serve as the main container for our layout elements.
- Inside the component's return statement, commence by embedding a
-
Constructing the Navigation:
- Begin by adding a navigation link for the homepage: use a list item (
li
) encapsulating aNavLink
component. This component, stemming fromreact-router-dom
, will navigate users to the root path (/
). To make the active link more distinguishable, utilize a callback function for itsclassName
prop to conditionally assign the class 'active'.
<li> <NavLink to="/" className={({ isActive }) => isActive ? 'active' : undefined}> Home </NavLink> </li>
- Display an additional option allowing them to add a product. Again, wrap a
NavLink
component inside a list item (li
). This link should navigate users to/add
, and also make use of the conditional class assignment.
- Begin by adding a navigation link for the homepage: use a list item (
-
Configuring the Main Content Rendering Area:
- Below the navigation, you should structure the main content area using a
main
tag. - The main content area is particularly special: it dynamically displays content based on the currently active route. To facilitate this, insert the
Outlet
component fromreact-router-dom
inside themain
section. Think of theOutlet
as a dynamic placeholder; it changes the rendered component according to the path.
- Below the navigation, you should structure the main content area using a
Objective: This section will guide you through creating a ProtectedRoute
component, which ensures that certain routes are only accessible to authenticated users. You'll be utilizing the useNavigate
hook from react-router-dom
for navigation within this component.
-
Using the
useNavigate
Hook:- Inside the
ProtectedRoute
component, initialize a variable callednavigate
using theuseNavigate
hook. This hook provides a function for programmatic navigation.
- Inside the
-
Checking Authentication:
- In the component, implement a conditional check inside a
useEffect
hook to determine if theuser
is authenticated. You can do this by checking ifuser
exists. Don't forget to pass to theuseEffect
hook dependencies array the required dependencies.
useEffect(() => { if (!user) { navigate("/"); } }, [navigate, user]);
- If the user is not authenticated (i.e.,
!user
), you want to redirect them to an appropriate page. In this example, it assumes a route'/'
as the redirect destination.
- In the component, implement a conditional check inside a
-
Redirecting Unauthenticated Users:
- To redirect users when they are not authenticated, use the
navigate
function with the appropriate route (e.g.,navigate('/')
). This will take them to the designated destination route.
- To redirect users when they are not authenticated, use the
-
Rendering Child Components for Authenticated Users:
- If the user is authenticated, you want to render the child components. Return
children
within curly braces{children}
. This allows the children components passed toProtectedRoute
to be rendered when the user is authenticated.
- If the user is authenticated, you want to render the child components. Return
Objective: In this section, we'll develop a Product
component that will display detailed information about a product based on its ID from the URL. We'll make use of the Link
and useParams
hooks from react-router-dom
to achieve this.
-
Extracting Product ID from the URL:
- Within the
Product
component, initialize a constant namedproductId
by destructuring it from theuseParams()
hook. This hook extracts parameters from the current route, and in our case, we're interested in the product's ID.
- Within the
-
Identifying the Relevant Product:
- With the
productId
at hand, our next step is to identify the corresponding product from theproducts
prop. - Declare a constant called
product
and set it to the result of thefind
method on theproducts
array. - The
find
method's callback should compare theid
of each product to theproductId
. Remember to convertproductId
to an integer usingparseInt
since parameters fetched usinguseParams
are strings by default.
- With the
-
Handling Product Absence:
- Considering that the product may not always exist (e.g., if an incorrect ID is manually input into the URL), we need to handle such scenarios gracefully.
- Implement a conditional check: If the
product
constant is undefined, return adiv
element with the text "Product not found!".
-
Facilitating Product Edits:
- Equip users with the capability to modify the product details.
- Implement the
Link
component fromreact-router-dom
to navigate to the product's edit page. - Set the
to
prop of theLink
component to a dynamic value:/products/${productId}/edit
. - Give the
edit-link
class to this link for styling and label the link "Edit Product".
Objective: The purpose of this section is to guide you through enhancing the AddProduct
component by adding navigation capabilities. This ensures that after a product is added, the user is navigated back to the home page or another appropriate page.
-
Setting Up Navigation:
- Invoke the
useNavigate
hook and assign the returned function to a constant namednavigate
. This function will be used to programmatically navigate the user.
- Invoke the
-
Constructing the
handleSubmit
Function with Navigation:- After logging the product and resetting the state, employ the
navigate
function with'/'
as its argument. This action will redirect the user to the home page after they've added a product.
- After logging the product and resetting the state, employ the
Objective: The purpose of this section is to enhance the EditProduct
component by integrating functionalities associated with React Router. By the end, you'll have a component that fetches the product to edit based on the ID from the URL and navigates back to the home page after the editing is done.
-
Retrieving the Product ID from the URL:
- Using the
useParams
hook fromreact-router-dom
, extract theproductId
parameter from the current URL.
- Using the
-
Setting Up Programmatic Navigation:
- Invoke the
useNavigate
hook fromreact-router-dom
and assign the returned function to a constant namednavigate
. This function will allow you to programmatically redirect users after the product edits have been saved.
- Invoke the
-
Fetching the Product Data Based on URL Parameter:
- With the
productId
extracted in step 1, search for the corresponding product within theproducts
mock data. Ensure you parse theproductId
to an integer for accurate comparison.
- With the
-
Initializing the Form Data with the Fetched Product's Information:
- Set the initial state of the
formData
using theuseState
hook. Populate it with the properties of the found product (i.e.,name
,description
,price
, andstock
).
const [formData, setFormData] = useState({ name: product.name, description: product.description, price: product.price, stock: product.stock, });
- Set the initial state of the
-
Enhancing the
handleSubmit
Function with Navigation:- After handling the form's submit action (like saving edits), employ the
navigate
function with'/'
as its argument. This step will ensure the user gets redirected to the home page after the edits have been submitted.
const handleSubmit = (e) => { e.preventDefault(); navigate(`/`); };
- After handling the form's submit action (like saving edits), employ the
Congratulations on completing this comprehensive React Router tutorial! Throughout this exercise, you learned how to build an effective routing solution using React Router. You covered topics ranging from configuring base routes, constructing child routes, handling undefined routes, setting up protected routes, implementing react router, building layout components, working with Link components, and much more. These skills will undoubtedly help you in your career as a developer and enable you to build robust, modern web applications that meet today's demands.