TIL/2021–02–13 & 14

Day 65 and 66: Treehouse Full Stack JavaScript Techdegree

React Router 4 Basics Continuation

Displaying 404 Error Routes using Switch

Currently in our directory app, once we type a URL that does not match any of the route paths, nothing renders

We should give feedback when something goes wrong - like a user visits the wrong url or mistypes the link

Web developers usually create a 404 error page that displays when the webpage the user is trying to reach cannot be found on the server

React router lets us create a 404 like error route that displays when a URL’s path does not match any of the paths defined in our routes

In our components directory we’ll see a file called NotFound.js that contains a stateless functional component that renders and error icon and text that says page not found

First let’s import the file in App.js

import NotFound from "./NotFound";

And we will create a new <Route />

And unlike the other routes we will not specify the path but just the component that will be rendered

<Route component={NotFound} />

Though a route without the path will always render the component

So to render the NotFound component only if the URL does not match any Route, we will use React Router’s Switch Component

So first let us import Switch from react-router-dom

import { BrowserRouter, Route, Switch } from "react-router-dom";

Then wrap all our Routes inside the Switch component

Switch will only render the first Route that matches the URL

For example, we placed a /teachers path on the URL and then switch will look for the first Route that has the /teachers path. And it will execute or render that Route.

If switch does not find any matching Route, it is going to fall back to the catch all route - which is our Route that renders the NotFound component

const App = () => (

<BrowserRouter>

<div className="container">

<Header />

<Switch>

<Route exact path="/" component={Home} />

<Route path="/about" render={() => <About />} />

<Route path="/teachers" component={Teachers} />

<Route path="/courses" component={Courses} />

<Route component={NotFound} />

</Switch>

</div>

</BrowserRouter>

);

And it works!

------

Passing Data to components via routes

Let’s refactor our Courses since they all display the same type of data

On HTML.js

import React from 'react';

import Course from './Course';

import { HTMLCourses } from '../../data/courses';

const HTML = (props) => {

let courses = HTMLCourses.map((course) => {

return <Course title={course.title}

desc={course.description}

img={course.img_src}

key={course.id} />

});

return (

<div>

<ul>

{courses}

</ul>

</div>

);

}

export default HTML;

Now turned CourseContainer

import React from "react";

import Course from "./Course";

const CourseContainer = (props) => {

let courses = props.data.map((course) => {

return (

<Course

title={course.title}

desc={course.description}

img={course.img_src}

key={course.id}

/>

);

});

return (

<div>

<ul>{courses}</ul>

</div>

);

};

export default CourseContainer;

Then in Courses.js

import React from "react";

import { NavLink, Route, Redirect } from "react-router-dom";

import { HTMLCourses, CSSCourses, JSCourses } from "../data/courses";

//App Components

import CourseContainer from "./courses/CourseContainer";

const Courses = ({ match }) => (

<div className="main-content courses">

<div className="course-header group">

<h2>Courses</h2>

<ul className="course-nav">

<li>

<NavLink to={`${match.url}/html`}>HTML</NavLink>

</li>

<li>

<NavLink to={`${match.url}/css`}>CSS</NavLink>

</li>

<li>

<NavLink to={`${match.url}/javascript`}>JavaScript</NavLink>

</li>

</ul>

</div>

{/* Write routes here... */}

<Route

exact

path={match.path}

render={() => <Redirect to={`${match.path}/html`} />}

/>

<Route

path={`${match.path}/html`}

render={() => <CourseContainer data={HTMLCourses} />}

/>

<Route

path={`${match.path}/css`}

render={() => <CourseContainer data={CSSCourses} />}

/>

<Route

path={`${match.path}/javascript`}

render={() => <CourseContainer data={JSCourses} />}

/>

</div>

);

export default Courses;

We changed our Route component prop to a render prop so we can pass data to the CourseContainer tag component

--------

Using URL parameters

With React Router we are able to declare dynamic routes using special URL parameters - the data passed in your URL parameters can be displayed on the app.

We can use URL parameters to create one route that matches a portion of a path dynamically like a teacher’s name and topic area.

Then we can extract that data from the URL - and render it inside the component

In our Components folder we have a Component called Featured.js and that is what renders the featured teacher page

First on App.js let’s import Featured

import Featured from "./Featured"

const App = () => (

<BrowserRouter>

<div className="container">

<Header />

<Switch>

<Route exact path="/" component={Home} />

<Route path="/about" render={() => <About />} />

<Route exact path="/teachers" component={Teachers} />

<Route path="/teachers" component={Featured} />

<Route path="/courses" component={Courses} />

<Route component={NotFound} />

</Switch>

</div>

</BrowserRouter>

);

So now the featured component will render when you include a subpath in the teachers URL

So to display content in our app from the data passed to the URL, we use special URL parameters

We define a parameter in a Route with a colon :

<Route path="/teachers/:name" component={Featured} />

Note: we can give the parameter any name we want as long as we include a colon in front of it

The parameters of the path are dynamic and made available to components via props - this means we are able to use what’s being passed as data in our component

We can access it through that match object - params property

So, on our featured.js we can call that match object by placing it in our Featured parameter as an object and get the name by: match.params.name

import React from "react";

const Featured = ({ match }) => {

const name = match.params.name;

return (

<div className="main-content">

<h2>{name} </h2>

<p>

Introducing <strong>{name}</strong>, a teacher who loves teaching

courses about <strong></strong>!

</p>

</div>

);

};

export default Featured;

And it works

Let’s add one more parameter - this time we’ll display the teacher’s topic area

<Route path="/teachers/:topic/:name" component={Featured} />

import React from "react";

const Featured = ({ match }) => {

const name = match.params.name;

const topic = match.params.topic;

return (

<div className="main-content">

<h2>{name} </h2>

<p>

Introducing <strong>{name}</strong>, a teacher who loves teaching

courses about <strong>{topic}</strong>!

</p>

</div>

);

};

export default Featured;

The Featured component is using the params being passed from the URL to render both renzo and javascript

And we can also create links to several featured teachers using any topic and name we want

On Home.js

import React, { Component } from "react";

import { Link } from "react-router-dom";

class Home extends Component {

render() {

return (

<div className="main-content home">

<h2>Front End Course Directory</h2>

<p>

This fun directory is a project for the <em>React Router Basics</em>{" "}

course on Treehouse.

</p>

<p>

Learn front end web development and much more! This simple directory

app offers a preview of our course library. Choose from many hours of

content, from HTML to CSS to JavaScript. Learn to code and get the

skills you need to launch a new career in front end web development.

</p>

<p>

We have thousands of videos created by expert teachers on web design

and front end development. Our library is continually refreshed with

the latest on web technology so you will never fall behind.

</p>

<hr />

<h3> Featured Teachers</h3>

<Link to="/teachers/JavaScript/Renzo">Renzo</Link>

</div>

);

}

}

export default Home;

And it works!

We can also separate URL parameters with a dash -

For example, to display a teacher’s first and last name

On App.js

<Route path="/teachers/:topic/:fname-:lname" component={Featured} />

On Featured.js

import React from "react";

const Featured = ({ match }) => {

const name = `${match.params.first} ${match.params.last}`;

const topic = match.params.topic;

return (

<div className="main-content">

<h2>{name} </h2>

<p>

Introducing <strong>{name}</strong>, a teacher who loves teaching

courses about <strong>{topic}</strong>!

</p>

</div>

);

};

export default Featured;

And it works!

To make a parameter optional, just add a ? at the end of the parameter name

<Route path="teachers/:topic/:fname/:lname?" component={Featured} />

For example, I will make the last name optional

<Route path="/teachers/:topic/:fname/:lname?" component={Featured} />

import React from "react";

const Featured = ({ match }) => {

const name = match.params.lname

? `${match.params.fname} ${match.params.lname}`

: match.params.fname;

const topic = match.params.topic;

return (

<div className="main-content">

<h2>{name} </h2>

<p>

Introducing <strong>{name}</strong>, a teacher who loves teaching

courses about <strong>{topic}</strong>!

</p>

</div>

);

};

export default Featured;

Then if there is a last name

------

Navigating Routes Programmatically

Most of the navigation in our app will be done using Link or the NavLink component - though there will be times when we’ll need to programmatically change the path in the url - a common example is changing the path in response of a form submission

So on our Home Component we will add a form - the idea is that when we place the information on the form and submit we will be redirected using the data submitted from the form

import React, { Component } from "react";

class Home extends Component {

render() {

return (

<div className="main-content home">

<h2>Front End Course Directory</h2>

<p>

This fun directory is a project for the <em>React Router Basics</em>{" "}

course on Treehouse.

</p>

<p>

Learn front end web development and much more! This simple directory

app offers a preview of our course library. Choose from many hours of

content, from HTML to CSS to JavaScript. Learn to code and get the

skills you need to launch a new career in front end web development.

</p>

<p>

We have thousands of videos created by expert teachers on web design

and front end development. Our library is continually refreshed with

the latest on web technology so you will never fall behind.

</p>

<hr />

<h3> Featured Teachers</h3>

<form>

<input type="text" placeholder="Name" />

<input type="text" placeholder="Topic" />

<button type="submit">Go!</button>

</form>

</div>

);

}

}

export default Home;

We can use refs to reference and access the input value

When used on an HTML element the ref attribute takes a callback function that receives the underlying DOM element as its argument, and in both cases it is the input - these two callbacks are executed immediately after the home component is mounted to the DOM - so when the inputs are rendered to the page, they return a reference which we can access with this.name and this.topic. And we can access their values with .value

<input

type="text"

placeholder="Name"

ref={(input) => (this.name = input)}

/>

<input

type="text"

placeholder="Topic"

ref={(input) => (this.topic = input)}

/>

React router passes rendered components information about the current path and URL the route is matching. The component also gets passed a history object that listens for changes in the current URL, keeps track of browser history and the number of entries in the history stack

History stack is the previously visited URLs - every time a user navigates to a new path, that URL is stored in the history stack

--

--