├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── Categories.js ├── Login.js ├── PrivateRoute.js ├── Product.js ├── Products.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Router v6: A Beginner's Guide 2 | 3 | [React Router](https://reactrouter.com/) is the de facto standard routing library for React. This repo consists of several examples that demonstrate what you can do with react router v6. 4 | 5 | The examples covered include: 6 | 7 | - Basic navigational routing 8 | - Nested routing 9 | - Nested routing with path parameters 10 | - Protected routing 11 | 12 | This code accompanies the following tutorial on SitePoint: https://www.sitepoint.com/react-router-complete-guide/ 13 | 14 | ## Requirements 15 | 16 | * [Node.js](http://nodejs.org/) 17 | 18 | ## Installation Steps (if applicable) 19 | 20 | 1. Clone repo 21 | 2. Run `npm install` 22 | 3. Run `npm run start` 23 | 4. Visit http://localhost:3000/ 24 | 25 | ## License 26 | 27 | SitePoint's code archives and code examples are licensed under the MIT license. 28 | 29 | Copyright © 2023 SitePoint 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-router-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-router-dom": "^6.18.0", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/react-router-demo/a2704ad976713a5a1f1d2e4f4658073498e3cee7/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/react-router-demo/a2704ad976713a5a1f1d2e4f4658073498e3cee7/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/react-router-demo/a2704ad976713a5a1f1d2e4f4658073498e3cee7/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { Link, Route, Routes } from 'react-router-dom'; 2 | import { Categories, Desktops, Laptops } from './Categories'; 3 | import Products from './Products'; 4 | import Login from './Login'; 5 | import PrivateRoute from './PrivateRoute'; 6 | 7 | const Home = () => ( 8 |
9 |

Home

10 |

Welcome to our homepage!

11 |
12 | ); 13 | 14 | const Admin = () => ( 15 |
16 |

Welcome admin!

17 |
18 | ); 19 | 20 | export default function App() { 21 | return ( 22 |
23 | 39 | 40 | 41 | } /> 42 | }> 43 | } /> 44 | } /> 45 | 46 | } /> 47 | } /> 48 | 52 | 53 | 54 | } 55 | /> 56 | 57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/Categories.js: -------------------------------------------------------------------------------- 1 | import { Link, Outlet } from 'react-router-dom'; 2 | 3 | export const Categories = () => ( 4 |
5 |

Categories

6 |

Browse items by category.

7 | 17 | 18 | 19 |
20 | ); 21 | 22 | export const Desktops = () =>

Desktop PC Page

; 23 | export const Laptops = () =>

Laptops Page

; 24 | -------------------------------------------------------------------------------- /src/Login.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { useNavigate, useLocation } from 'react-router-dom'; 3 | 4 | export default function Login() { 5 | const navigate = useNavigate(); 6 | const { state } = useLocation(); 7 | const from = state?.from || { pathname: '/' }; 8 | const [redirectToReferrer, setRedirectToReferrer] = useState(false); 9 | 10 | const login = () => { 11 | fakeAuth.authenticate(() => { 12 | setRedirectToReferrer(true); 13 | }); 14 | }; 15 | 16 | useEffect(() => { 17 | if (redirectToReferrer) { 18 | navigate(from.pathname, { replace: true }); 19 | } 20 | }, [redirectToReferrer, navigate, from.pathname]); 21 | 22 | return ( 23 |
24 |

You must log in to view the page at {from.pathname}

25 | 26 |
27 | ); 28 | } 29 | 30 | /* A fake authentication function */ 31 | export const fakeAuth = { 32 | isAuthenticated: false, 33 | authenticate(cb) { 34 | this.isAuthenticated = true; 35 | setTimeout(cb, 100); 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /src/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useNavigate, useLocation } from 'react-router-dom'; 3 | import { fakeAuth } from './Login'; 4 | 5 | const PrivateRoute = ({ children }) => { 6 | const navigate = useNavigate(); 7 | const location = useLocation(); 8 | 9 | useEffect(() => { 10 | if (!fakeAuth.isAuthenticated) { 11 | navigate('/login', { 12 | state: { from: location }, 13 | replace: true 14 | }); 15 | } 16 | }, [navigate, location]); 17 | 18 | return fakeAuth.isAuthenticated ? children : null; 19 | }; 20 | 21 | export default PrivateRoute; 22 | -------------------------------------------------------------------------------- /src/Product.js: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router-dom'; 2 | 3 | const Product = ({ data }) => { 4 | const { productId } = useParams(); 5 | const product = data.find((p) => p.id === Number(productId)); 6 | 7 | return ( 8 |
9 | {product ? ( 10 |
11 |

{product.name}

12 |

{product.description}

13 |
14 |

{product.status}

15 |
16 | ) : ( 17 |

Sorry. Product doesn't exist.

18 | )} 19 |
20 | ); 21 | }; 22 | 23 | export default Product; 24 | -------------------------------------------------------------------------------- /src/Products.js: -------------------------------------------------------------------------------- 1 | import { Link, Route, Routes } from "react-router-dom"; 2 | import Product from "./Product"; 3 | 4 | const productData = [ 5 | { 6 | id: 1, 7 | name: "Dell OptiPlex 3090", 8 | description: 9 | "The Dell OptiPlex 3090 is a compact desktop PC that offers versatile features to meet your business needs.", 10 | status: "Available", 11 | }, 12 | { 13 | id: 2, 14 | name: "Lenovo ThinkPad X1 Carbon", 15 | description: 16 | "Designed with a sleek and durable build, the Lenovo ThinkPad X1 Carbon is a high-performance laptop ideal for on-the-go professionals.", 17 | status: "Out of Stock", 18 | }, 19 | { 20 | id: 3, 21 | name: "CyberPowerPC Gamer Xtreme", 22 | description: 23 | "The CyberPowerPC Gamer Xtreme is a high-performance gaming desktop with powerful processing and graphics capabilities for a seamless gaming experience.", 24 | status: "Available", 25 | }, 26 | { 27 | id: 4, 28 | name: "Apple MacBook Air", 29 | description: 30 | "The Apple MacBook Air is a lightweight and compact laptop with a high-resolution Retina display and powerful processing capabilities.", 31 | status: "Out of Stock", 32 | }, 33 | ]; 34 | 35 | const Products = () => { 36 | const linkList = productData.map((product) => { 37 | return ( 38 |
  • 39 | {product.name} 40 |
  • 41 | ); 42 | }); 43 | 44 | return ( 45 |
    46 |

    Products

    47 |

    Browse individual products.

    48 | 49 | 50 | 51 | } /> 52 | Please select a product.

    } /> 53 |
    54 |
    55 | ); 56 | }; 57 | 58 | export default Products; 59 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | --------------------------------------------------------------------------------