<info 340/>

React Router

Joel Ross
Autumn 2024

View of the Day

  • React Libraries (code demo)

  • Single Page Applications (lecture)

  • React Router (code demo)

  • URL Parameters (lecture + code demo)

  • Firebase Hosting (demo)

Project Draft 2

What we are looking for: Refactored Draft 1 into a React App 

Converted the HTML/CSS from draft 1 into a published React app. Began to add interactive functionality.

  • ALL of content rendered: You should have Components that render DOM for every part of your page
  • Has Components w/ props and data: Organize your Components! Can hard-code sample data for now
  • Uses routes to show all page content ("next" week)
  • Has 1 feature implemented: Includes event handling and state manipulation (today). Can be the small feature!
  • Fixes issues from draft 1: You're revising the HTML/CSS, fix problems while you're at it!
  • Published to Firebase hosting: get that working this draft
    (see assignment for details; demo today!)
# switch to starter branch to get new starter code
git checkout starter

# download new starter code
git pull

# switch back to main branch for coding
git checkout main

# merge in new starter code (use default msg)
git merge starter --no-edit

# code and enjoy!

Get the starter code from the starter branch, but do all of your work on main.

Updating Lecture Code

Our chat app so far...

React Libraries

React components are structured to be self-contained, re-usable elements... so there are lots of pre-defined components online you can use!

In order to use a component in your app:

  1. Find it! (via npm, google, etc). Read the documentation
  2. Install it! npm install lib-name
  3. Import it! import { ComponentName } from 'lib-name'
    • (import structure may vary per library)
  4. Render it! <ComponentName />
    • Remember to pass any expected props!

Single-Page Applications

Client-Side Routing

Render a different component depending on the URL.

"IF the current url MATCHES a route, render this Component!"

function App(props) {
  //pick a component based on the URL
  let componentToRender = null;
  if(currentUrl === '/home'){ //pseudocode comparison with URL
    componentToRender = <HomePage />;
  }
  else if(currentUrl === '/about'){
    componentToRender = <AboutPage />;
  }

  //render that component
  return <div>{componentToRender}</div>;
}

react-router

A library of React Components that can determine which other components to render based on the current URL.

Install Library

Import Components

//in App.js
import { Routes, Route } from 'react-router-dom';
# Install library (on command line)
npm install react-router-dom

as of Nov 2021

Router Versions

The version we're targeting:

Adds data apis (not using)

Other changes since then are mostly not relevant for us.

BrowserRouter

The BrowserRouter component will keep track of the current URL in its state, and re-renders descendent components if that changes.

//index.js

import { BrowserRouter } from 'react-router-dom'
import App from './components/App.js'

//render the App *inside* of the BrowserRouter
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Routes

Pass elements in a Route Component to specify that they should only render when the URL matches a particular path. All routes go inside of a Routes element, which chooses which to "match" based on the URL

function App(props) {
  return (
    <Routes> {/* the collection of routes to match */}
      {/* if currentUrlPath === "home" */}
      <Route path="home" element={<HomePage />} />

      {/* else if currentUrlPath === "about" */}
      <Route path="about" element={<AboutPage />} />
    </Routes>
  );
}

Links

Use a Link element (in place of an <a>) to create state-based links between routes.

function Nav() {
  return (
    <nav>
      <ul>
        <li>
          {/* replaces anchor element */}
    	  <Link to="home">Home</Link>
        </li>
        <li>
          <Link to="about">About</Link>
        </li>
      </ul>
    </nav>
  );
}

URL Format

Like postal addresses, URLs follow a particular format.

  • scheme (protocol) how to access the information
  • domain which web service has the resource
  • path what resource to access
  • query extra parameters (arguments) to the request

format: ?key=value&key=value&key=value

Naming Routes

The web is based on the REST architecture. In this structure, each route (identifier, URI) should refer to a unique resource (set of information).

Think about what "information" should be found at each route. Come up with your routes first, and decide the components second!

function App(props) {
  return (
    <Routes>
      {/* good routes */}
      <Route path="/products" element={<AllProductsPage />} />
      <Route path="/products/hat" element={<HatPage />} />
      <Route path="/products/shoes" element={<ShoesPage />} />
      <Route path="/account" element={<AccountPage />} />
        
      {/* less good route -- an action, not a resource! */}
	  {/* (bad component definition as well) */}
      <Route path="/purchase" element={<ProcessPurchase />} />
    </Routes>
  )
}

Nesting Routes

The Route path corresponds to a segment of a URL, with nested Route elements corresponding to each segment. Nested Routes will render in place of an Outlet component

function App(props) {
  return (
    <Routes>
      <Route path="user" element={<UserLayout />} >
        <Route path="profile" element={<UserProfile />} />
        <Route path="favorites" element={<FavoriteItems />} />
      </Route>
      <Route path="items" element={<ItemList />} />
    </Routes>  
  );
}
function UserLayout(props) {
  render (
    <div className="user-layout">
      <h1>User Page</h1>
      <p>Layout specific content...</p>
      <Outlet /> {/* will be replaced with <UserProfile/>, etc */}
    </div>
  )
}

Protected Routes

A common use of nested routes is to make some routes protected, only showing content if e.g., the user is logged in.

function RequireAuth(props) {
  //...determine if user is logged in
  if(!userIsLoggedIn) { //if no user, say so
    return <p>Forbidden!</p>
  }
  else { //otherwise, show the child route content
    return <Outlet />
  }
}

function App(props) {
  return (
    <Routes>
      {/* protected routes */}
      <Route element={<RequireAuth />}>
        <Route path="profile" element={<ProfilePage />} />
        <Route path="secret" element={<SecretPage />} />
      </Route>
      {/* public routes */}
      <Route path="signin" element={<SignInPage />} />
    </Routes>
  )
}

"Variable" Routes

Sometimes you have a multiple routes that show the same component, just for different data--where that data is specified by one of the segments!

function App(props) {
  return (
    <Routes>
      <Route path="/products" element={<AllProductsPage />} />

      {/* routes go to same "view", just different content based on url */}
      <Route path="/products/hat" element={<ProductDetails item={"hat"} />} />
      <Route path="/products/shoes" element={<ProductDetails item={"shoes"} />} />

	</Routes>
  )
}

A url parameter is a "variable" in the url's path. This is a shortcut for defining a large number of routes that point to (similar) resources.

  • URL parameters are different than query params

URL Parameters

/users/:username

/users/:username/repos

/orgs/:owner/:repo

/users/{username}/repos

A variable

Two variables

Alternate notation

URL Params

Use :param syntax in the path to specify URL parameters. The useParams() hook lets you access the value of those parameters in the rendered element.

function App(props) {
  return (
    <Routes>
      {/* if currentUrl == "posts/______" */}
      {/* the string in the "blank" portion will be the
        * `postId` param */}
      <Route path="posts/:postId" element={<BlogPost />} />
    </Routes>
  );
}
import { useParams } from 'react-router-dom';

function BlogPost(props) {
  const urlParams = useParams(); //access the URL params as an object
  const postId = urlParams.postId; //can use destructuring instead
    
  return (
    {/* postId was the URL parameter from above! */}
    <h1>You are looking at blog post {urlParams.postId}</h1>
  )
}

Hosting on Firebase

GitHub pages is not able to cleanly handle client-side routing, so we'll use Firebase to host your projects instead!

Firebase is a web backend solution; it provides multiple features which you can access without need to "code" them yourselves.

  • Web Hosting
  • Databases
  • User Authentication

next weeks

React Deployment to Firebase

Use a combination of firebase command line tools and create-react-app scripts to build and deploy your React application to Firebase hosting!

 

See Project Draft 2 instructions on Canvas for details.

Action Items!

  • Complete task list for Week 9 (items 1-6 !!)

  • Read Ch 18: AJAX Requests

    • ​(needs some updating)

    • we'll apply to React in lecture

  • Problem Set 08 due Wednesday (​it is small)

  • Project Draft 2 due FRIDAY!!!!


Next time: Working with Data: AJAX