In this project we will be building a site for a University to keep track of student information and class enrollment. We will be using React Router to navigate between the various views. Take some time to familiarize yourself with the provided components.
Appwill be the top level component for our application.Homewill be the home page displayed when the application first loads. It will also display the available classes.Aboutwill be the about page displaying information about the University.Historywill be a nested view with the about page and will display the history of the university.Contactwill be a nested view with the about page and will display the University's contact information.
ClassListwill display all the enrolled students for that specific class.Studentwill be the detail view for a particular student.
Live Example: Here
forkandclonethis repository.cdinto the project.- Run
npm install.- One of the packages that was installed is
json-server. - This library will mimic a REST api and allow you to make HTTP requests for
the student data.
- This data is stored in
./db.json.
- This data is stored in
- Another package that was installed for you is
concurrently. - This library will allow us to run multiple scripts in a single terminal window.
- One of the packages that was installed is
- Run
npm run start:allto spin up the development server AND thejson-server.
In this step, we'll be installing additional dependencies required for configuring a react router. We'll also set this package up so we can start building routes.
-
Install React Router
npm install react-router-dom -
Open
./src/App.js. -
Import
HashRouterfromreact-router-dom. -
Wrap the outer most
divcomponent in aHashRoutercomponent.
Detailed Instructions
Let's begin by installing the react-router-dom package. This will allow us to
use routing in a react application. We can install it by running
npm install react-router-dom in our terminal.
Now let's open ./src/App.js and import HashRouter from react-router-dom at
the top of the component.
import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom';We'll need to wrap our react application with this HashRouter component in
order for routing to work.
export default class App extends Component {
render() {
return (
<HashRouter>
<div>
<nav className="nav">
<div>WestSide University</div>
<div className="link-wrap">
<div className="links">Home</div>
<div className="links">About</div>
</div>
</nav>
</div>
</HashRouter>
);
}
}Now that our application is wrapped with HashRouter, we can render our router
that we will create in the next step.
./src/App.js
import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom';
export default class App extends Component {
render() {
return (
<HashRouter>
<div>
<nav className="nav">
<div>WestSide University</div>
<div className="link-wrap">
<div className="links">Home</div>
<div className="links">About</div>
</div>
</nav>
</div>
</HashRouter>
);
}
}Next we'll create a router for the project.
- Create a new file in
./srccalledroutes.jsand open it. - Configure a router in this file:
- Use the following components as routes:
./src/components/Home/Home.js./src/components/About/About.js
- Use the following combinations of paths and components for your routes:
- Path: "/" - Component:
Home- This path should be exact. - Path: "/about" - Component:
About.
- Path: "/" - Component:
- Use the following components as routes:
- Open
./src/App.js. - Import
routesfrom./routes.js. - Render the
routesJSX underneath thenavelement.
Detailed Instructions
Now that we have our HashRouter set up, let's create a JavaScript file that
will hold all of our routes. In .src/, let's make a new file called
routes.js. At the top of this file we'll need to import React from react
and also import Switch and Route from react-router-dom.
import React from 'react';
import { Route, Switch } from 'react-router-dom';We'll also want to import the components that we want the user to be able to
route to. In this case, let's import the Home and About component.
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';Now that we have all of our dependencies in routes.js, we can use an
export default statement to export a router. We'll need an exact route at
/ to load the Home component and a route at /about to load the About
component.
export default function Routes() {
return (
<Switch>
<Route exact component={Home} path="/" />
<Route component={About} path="/about" />
</Switch>
);
}Then finally we need to actually render our router. Open App.js and import our
routes file.
import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom';
import Routes from './routes';Add the Routes component to the render method, right below the nav element.
<HashRouter>
<div>
<nav className="nav">
<div>WestSide University</div>
<div className="link-wrap">
<div className="links">Home</div>
<div className="links">About</div>
</div>
</nav>
<Routes />
</div>
</HashRouter> ./src/routes.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';
export default function Routes() {
return (
<Switch>
<Route exact component={Home} path="/" />
<Route component={About} path="/about" />
</Switch>
);
} ./src/App.js
import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom';
import Routes from './routes';
export default class App extends Component {
render() {
return (
<HashRouter>
<div>
<nav className="nav">
<div>WestSide University</div>
<div className="link-wrap">
<div className="links">Home</div>
<div className="links">About</div>
</div>
</nav>
<Routes />
</div>
</HashRouter>
);
}
}In this step, we will be adding links to render our home and about views.
- Open
src/App.js. - Import
Linkfromreact-router-dom. - Locate the
divelements with className of links.- Replace the
divwithLinkcomponents. - The Home link should be sent to
/. - The About link should be sent to
/about.
- Replace the
Detailed Instructions
Let's begin by opening src/App.js and importing Link from react-router-dom
at the top of the file. The Link component will allow us to add clickable
links into the DOM so the user can navigate the application. There are two div
elements with the classname of links. Let's replace the div elements with
Link elements, keep the same classname. The Link component uses a to prop
to determine which route to navigate to. For the home route, we'll want to use
/, and for the about route, we'll want to use /about.
<Link to="/" className="links">Home</Link>
<Link to="/about" className="links">About</Link> ./src/App.js
import React, { Component } from 'react';
import { HashRouter, Link } from 'react-router-dom';
import Routes from './routes';
export default class App extends Component {
render() {
return (
<HashRouter>
<div>
<nav className="nav">
<div>WestSide University</div>
<div className="link-wrap">
<Link to="/" className="links">
Home
</Link>
<Link to="/about" className="links">
About
</Link>
</div>
</nav>
<Routes />
</div>
</HashRouter>
);
}
}In this step, we will be adding a new route for our ClassList component. We
will also be adding Link components in the Home component to link to the
ClassList route for each of the listed classes ( Math, English, and Biology ).
The ClassList component will need to render students for a specific class, in
order to do this we'll be using route parameters.
- Open
src/routes.js. - Import the
ClassListcomponent to use as a route. - Create a
ClassListroute with the following properties:- Path:
/classlist/:class- Component:ClassList.
- Path:
- Open
src/Home/Home.js. - Import
Linkfromreact-router-dom. - Wrap each
buttonelement with aLinkcomponent. - Each link should direct to the path
/classlistwith the class parameter.- Math 1010 -
/classlist/MATH1010 - English 2010 -
/classlist/ENG2010 - Biology 2020 -
/classlist/BIO2020
- Math 1010 -
Detailed Instructions
Let's begin by opening src/routes.js and importing the ClassList at the top
of the file with the other imported components. For this component, we are going
to make use of routing parameters. This will allow us to use a single component
that can know what dataset to load by looking at the parameter. For example: the
math parameter will be MATH1010, when the component loads and reads MATH1010
we can select all the students from db.json with the class of MATH1010. If
that doesn't make sense entirely don't worry, we'll go into more detail in later
steps.
For now let's add a new route that uses a path of /classlist/:class and uses a
component of ClassList. The :class indicates a route parameter called
class in the url. We'll cover how to access the route parameter in a later
step.
<Route component={ClassList} path="/classlist/:class" />Now that we have our new route setup in ./src/routes.js, let's open up
./src/components/Home/Home.js and import Link from react-router-dom at the
top of the file. The Home component renders three buttons for the classes,
let's update those buttons to be wrapped in a Link component. For Math, we'll
want to route /classlist/MATH1010. For English, we'll want to route to
/classlist/ENG2010. And for Biology, we'll want to route to
/classlist/BIO2020. If you're wondering why it's specifically MATH1010,
ENG2010, and BIO2020; it's so that we can map over the db.json and make a
class match. A student's class property will equal one of those three
strings.
render() {
return (
<div className="box">
<Link to="/classlist/MATH1010">
<button className="btn">Math 1010</button>
</Link>
<Link to="/classlist/ENG2010">
<button className="btn">English 2010</button>
</Link>
<Link to="/classlist/BIO2020">
<button className="btn">Biology 2020</button>
</Link>
</div>
);
} ./src/routes.js
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';
import ClassList from './components/ClassList/ClassList';
export default function Routes() {
return(
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/classlist/:class" component={ClassList} />
</Switch>
)
} ./src/components/Home/Home.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Home extends Component {
render() {
return (
<div className="box">
<Link to="/classlist/MATH1010">
<button className="btn">Math 1010</button>
</Link>
<Link to="/classlist/ENG2010">
<button className="btn">English 2010</button>
</Link>
<Link to="/classlist/BIO2020">
<button className="btn">Biology 2020</button>
</Link>
</div>
);
}
}Watch how the URL changes when navigating between pages
In this step, we will update the ClassList component to display the students
enrolled for that specific class. To get this data, we will look at what the
class route parameter equals an make a HTTP request to our json-server.
- Open
src/components/ClassList/ClassList.js. - Create a constructor method that initializes state with a property called
students.studentsshould default to an empty array.
- Create a
componentDidMountmethod that makes aHTTPrequest to thejson-server:-
Install
axiosandimportit into the component.npm install axios -
The
json-serverAPI url ishttp://localhost:9090/students?class=.- Class should equal
MATH1010ORENG2010ORBIO2020depending on the route parameter. - Hint:
react-router-dompasses down amatchobject on a component'sprops.
- Class should equal
-
Use the returned data from the API request to update the
studentsarray onstate.
-
- Go into the
rendermethod of the component. mapover the students and return anh3tag of the studentsfirstandlastname.- Remember react requires a unique
keyprop on mapped elements. - The properties are called
first_nameandlast_name.
- Remember react requires a unique
- Undearneath the
h2tag, render themappedover students. - Update the
h1tag to display the page's class name.- Hint:
react-router-dompasses down amatchobject on a component'sprops.
- Hint:
Detailed Instructions
Let's begin by opening ./src/components/ClassList/ClassList.js. In the
constructor method, let's initialize state to have a property called
students that equals an empty array. This is where we'll store our returned
data from hitting the json-server.
constructor() {
super();
this.state = {
students: []
};
}Now that we have somewhere to store our API results, let's focus on the code to
actually make the API request. First, we'll need axios. Let's use
npm install --save axios to add it to our project and also import it at the
top of our component. After that, let's create a componentDidMount lifecycle
method. This will allow us to get our students as soon as possible.
componentDidMount() {
}Since we are fetching data, let's make a GET request to the API url:
http://localhost:9090/students?class= where class equals the current class
page. react-router-dom automatically passes down a couple handy props into the
routeable components. One of them is called match. It is an object with a
bunch of useful information. One of the properties on match is called
params. This is where we can see the value of any route parameters. Our
route parameter is called class. Therefore, we can access it by using
this.props.match.params.class.
That means our GET request url should look like:
http://localhost:9090/students?class=${this.props.match.params.class}. We
can then capture the results of this API request and use setState to update
the value of students to be the result's data.
axios
.get(`http://localhost:9090/students?class=${this.props.match.params.class}`)
.then(results => {
this.setState({
students: results.data,
});
});Now that we have our students coming in from our json-server, let's use a
map in the render method to render each student's first and last name in a
h3 element. Remember the react requires mapped elements to have a unique key
property. In this case, we'll just use the index of the map.
render() {
return (
<div className="box">
<h1>{this.props.match.params.class}</h1>
<h2>ClassList:</h2>
{this.state.students.map((student, i) => (
<h3 key={student.id}>
{student.first_name} {student.last_name}
</h3>
))}
</div>
);
}Lastly, we just need to update the h1 element to display the current class.
Just like how we accessed the route parameter for our axios request, we can do
the same thing here using { } in our JSX.
<h1>{this.props.match.params.class}</h1> ./src/components/ClassList/ClassList
import React, { Component } from 'react';
import axios from 'axios';
export default class ClassList extends Component {
constructor(props) {
super(props);
this.state = {
students: [],
};
}
componentDidMount() {
axios
.get(`localhost:9090/students?class=${this.props.match.params.class}`)
.then(response => this.setState({ students: response.data }));
}
render() {
return (
<div className="box">
<h1>{this.props.match.params.class}</h1>
<h2>ClassList:</h2>
{this.state.students.map((student, i) => (
<h3 key={student.id}>
{student.first_name} {student.last_name}
</h3>
))}
</div>
);
}
}In this step, we will start setting up the a student detail view in the
./src/components/Student/Student.js component. The Student component will
need to render any given student, in order to this we'll be using route
parameters for a student's ID.
- Open
./src/routes.js. - Import the
Studentcomponent to use as a route. - Create a
Studentroute with the following properties:- Path:
/student/:id- Component:Student.
- Path:
- Open
./src/components/ClassList/ClassList.js. - Import
Linkfromreact-router-dom. - Wrap the
h3tag with aLinkcomponent. - Assign the
toprop for theLinkcomponent to/student/:id, whereidshould equal the student's ID.- Remember to move the unique
keyprop to the outer most element of the map.
- Remember to move the unique
Detailed Instructions
Let's begin by opening ./src/routes.js and import the Student component at
the top of the file with the other components. We'll need to make a new route
for this Student component that uses an id route parameter. Similarly to how
we did it with the ClassList component, we can use axios to fetch a specific
student on load by making a match to the id property. For example, if id
equaled 1 we could fetch a student where the id equaled 1. The path for
this route should be /student/:id.
<Route component={Student} path="/student/:id" />Now that we have our Student route setup, let's open the ClassList component
and import the Link component from react-router-dom. We'll need to update
our map in the render method to wrap the h3 element in a Link component.
In this map, we have access to all the student's properties. Therefore, if we
need the id, we can access it by student.id. Let's set the to prop of the
Link component to be /student/${ student.id }. We'll also need to move the
key prop onto the Link component since it'll now be the most parent item.
render() {
return (
<div className="box">
<h1>{this.props.match.params.class}</h1>
<h2>ClassList:</h2>
{this.state.students.map((student, i) => (
<Link key={student.id} to={`/student/${student.id}`}>
<h3 key={student.id}>
{student.first_name} {student.last_name}
</h3>
</Link>
))}
</div>
);
} ./src/routes.js
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';
import ClassList from './components/ClassList/ClassList';
import Student from './components/Student/Student';
export default function Routes() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/classlist/:class" component={ClassList} />
<Route path="/student/:id" component={Student} />
</Switch>
);
} ./src/components/ClassList/ClassList.js
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
export default class ClassList extends Component {
constructor(props) {
super(props);
this.state = {
students: [],
};
}
componentDidMount() {
axios
.get(
`http://localhost:9090/students?class=${this.props.match.params.class}`
)
.then(response => this.setState({ students: response.data }));
}
render() {
return (
<div className="box">
<h1>{this.props.match.params.class}</h1>
<h2>ClassList:</h2>
{this.state.students.map((student, i) => (
<Link key={student.id} to={`/student/${student.id}`}>
<h3 key={student.id}>
{student.first_name} {student.last_name}
</h3>
</Link>
))}
</div>
);
}
}Watch how the URL changes when navigating between pages
In this step, we'll update the Student component to display a specific
student's information. To get this data, we'll look at the id route parameter
and use it in combination with an axios HTTP request to our json-server.
- Open
src/components/Student/Student.js. - Create a
constructormethod that initializes state with a property calledstudentInfo.studentInfoshould default to an empty object.
- Create a
componentDidMountmethod that makes anHTTPrequest to thejson-server:- Import
axiosinto the component. - The
json-serverAPI url ishttp://localhost:9090/students/ID_GOES_HERE.ID_GOES_HEREshould equal the student's id.- Hint:
react-router-dompasses down amatchobject on a component'sprops.
- Use the returned data from the API request to update the
studentInfoobject onstate.
- Import
- Go into the
rendermethod of the component. - Underneath the
h1tag, displaystudentInfoproperties off ofstate:first_nameandlast_namewithin anh1tag.- The text
Grade:followed by thegradeproperty within anh3tag. - The text
Email:followed by theemailproperty within anh3tag.
Detailed Instructions
Let's begin by opening src/components/Student/Student.js and import axios at
the top of the file with the other imports. We'll need a place to store our data
from hitting the API, let's update the constructor method to have a property
called studentInfo that defaults to an empty object.
constructor(props) {
super(props);
this.state = {
studentInfo: {}
};
}Now that we have somewhere to store the data, let's create a componentDidMount
method. Inside this method let's use axios to make a GET request to
http://localhost:9090/students/ID_GOES_HERE. Similarly to ClassList, we can
access the id for the given student on the match object that
react-router-dom passes into our routeable components. Therefore, our API url
would look like: http://localhost:9090/students/${this.props.match.params.id}.
Let's capture the results of this GET request and use the data to update the
value of studentInfo on state.
componentDidMount() {
axios
.get(`http://localhost:9090/students/${this.props.match.params.id}`)
.then(response => this.setState({ studentInfo: response.data }));
}Now that we have the student data coming in, we can go into the render method
and display the pieces of the student's information. Let's put the student's
first_name and last_name within a h1 tag and let's put the grade and email
in their own h3 tags.
render() {
return (
<div className='box'>
<h1>Student:</h1>
<h1>
{this.state.studentInfo.first_name} {this.state.studentInfo.last_name}
</h1>
<h3>Grade: {this.state.studentInfo.grade}</h3>
<h3>Email: {this.state.studentInfo.email}</h3>
</div>
)
} ./src/components/Student/Student.js
import React, { Component } from 'react';
import axios from 'axios';
export default class Student extends Component {
constructor(props) {
super(props);
this.state = {
studentInfo: {},
};
}
componentDidMount() {
axios
.get(`http://localhost:9090/students/${this.props.match.params.id}`)
.then(response => this.setState({ studentInfo: response.data }));
}
render() {
return (
<div className="box">
<h1>Student</h1>
<h1>
{this.state.studentInfo.first_name} {this.state.studentInfo.last_name}
</h1>
<h3>Grade: {this.state.studentInfo.grade}</h3>
<h3>Email: {this.state.studentInfo.email}</h3>
</div>
);
}
}In this step, we'll add a sub nav bar that will link to the soon to be nested
About, History, and Contact components.
- Open
src/components/About/About.js. - Import
Linkfromreact-router-dom. - Inside the
divwith the classNamesubnav, add 3h3tags with the text:AboutHistoryContact
- Wrap each
h3tag with aLinkcomponent that links the following paths:- About -
/about - History -
/about/history - Contact -
/about/contact
- About -
- Give each of these
Linkcomponents the classNamesubnav_links.
./src/components/About/About.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class About extends Component {
render() {
return (
<div>
<div className="subnav">
<Link to="/about" className="subnav_links">
<h3>About</h3>
</Link>
<Link to="/about/history" className="subnav_links">
<h3>History</h3>
</Link>
<Link to="/about/contact" className="subnav_links">
<h3>Contact</h3>
</Link>
</div>
<div className="box" />
</div>
);
}
}In this step, we'll create a sub router that routes to the About, History,
and Contact components. This step will avoid using detailed instructions to
provide a little bit of a challenge. Use what you've learned from the previous
steps to accomplish this one. If you get stuck, you can look at the
solution code.
-
Open
src/components/About/About.js. -
Import
SwitchandRoutefromreact-router-dom. -
Import the
HistoryandContactcomponents. -
Inside the
divwith the classNamebox, add aSwitchcomponent. -
Add 3 routes inside the
Switchcomponent:-
The first two should route the
HistoryandContactcomponents.- Hint: the paths for these components are the same values used in the
toprop on theLinkcomponents.
- Hint: the paths for these components are the same values used in the
-
The third route should render
JSX( instead of a component ) at the exact path of/about. -
To render JSX instead of a component you can use a
renderprop that equals a function that returns JSX.-
About JSX<div> <h1 className="title">About WestSide University:</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod eu lorem et ultricies. In porta lorem at dui semper porttitor. Nullam quis cursus dui. Cras tincidunt vehicula tellus eu facilisis. Donec nisi turpis, iaculis et arcu a, aliquet ultrices nisl. Nam in pharetra odio, ac blandit metus. Suspendisse potenti. Praesent elementum diam non orci cursus rutrum. Pellentesque condimentum ultrices dignissim. Sed a tempor ligula, vel luctus sapien. Mauris vehicula rutrum massa. Duis condimentum, ex quis ullamcorper rhoncus, erat libero tempor arcu, condimentum facilisis tellus lectus ut nunc. Pellentesque vitae faucibus diam. Vestibulum eu erat ex. Ut justo neque, varius aliquet erat vel, scelerisque convallis lacus. Mauris semper lorem mauris, sed dignissim eros consectetur nec. </p> </div>
-
-
./src/components/About/About.js
import React, { Component } from 'react';
import { Link, Switch, Route } from 'react-router-dom';
import History from '../History/History';
import Contact from '../Contact/Contact';
export default class About extends Component {
render() {
return (
<div>
<div className="subnav">
<Link to="/about" className="subnav_links">
<h3>About</h3>
</Link>
<Link to="/about/history" className="subnav_links">
<h3>History</h3>
</Link>
<Link to="/about/contact" className="subnav_links">
<h3>Contact</h3>
</Link>
</div>
<div className="box">
<Switch>
<Route path="/about/history" component={History} />
<Route path="/about/contact" component={Contact} />
<Route
exact
path="/about"
render={() => (
<div>
<h1>About the University</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
euismod eu lorem et ultricies. In porta lorem at dui semper
porttitor. Nullam quis cursus dui. Cras tincidunt vehicula
tellus eu facilisis. Donec nisi turpis, iaculis et arcu a,
aliquet ultrices nisl. Nam in pharetra odio, ac blandit
metus. Suspendisse potenti. Praesent elementum diam non orci
cursus rutrum. Pellentesque condimentum ultrices dignissim.
Sed a tempor ligula, vel luctus sapien. Mauris vehicula
rutrum massa. Duis condimentum, ex quis ullamcorper rhoncus,
erat libero tempor arcu, condimentum facilisis tellus lectus
ut nunc. Pellentesque vitae faucibus diam. Vestibulum eu
erat ex. Ut justo neque, varius aliquet erat vel,
scelerisque convallis lacus. Mauris semper lorem mauris, sed
dignissim eros consectetur nec.
</p>
</div>
)}
/>
</Switch>
</div>
</div>
);
}
}Try adding a back button on the Student detail view that will route back to
the ClassList view. You can also add a back button to the ClassList view
that will route back to the Home view.