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.
App
will be the top level component for our application.Home
will be the home page displayed when the application first loads. It will also display the available classes.About
will be the about page displaying information about the University.History
will be a nested view with the about page and will display the history of the university.Contact
will be a nested view with the about page and will display the University's contact information.
ClassList
will display all the enrolled students for that specific class.Student
will be the detail view for a particular student.
fork
andclone
this repository.cd
into 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 start
to 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 then create a router for the project.
- Install React Router (
npm install --save react-router-dom
). - Create a new file in
./src
calledroutes.js
and 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:
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 --save react-router-dom
in a terminal. Make sure the terminal is cd
into the root project directory.
Now that we have react-router-dom
, 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 { Switch, Route } 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 (
<Switch>
<Route component={ Home } exact path="/" />
<Route component={ About } path="/about" />
</Switch>
)
./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 (
<Switch>
<Route component={ Home } exact path="/" />
<Route component={ About } path="/about" />
</Switch>
)
In this step, we will take the routes we just configured in ./src/routes.js
and add it to our application in ./src/index.js
.
- Open
./src/index.js
. - Import
HashRouter
fromreact-router-dom
. - Wrap the
App
component in aHashRouter
component. - Open
./src/App.js
. - Import
routes
from./routes.js
. - Underneath the
nav
element render theroutes
JSX.
Detailed Instructions
Let's begin by opening ./src/index.js
and importing HashRouter
from react-router-dom
at the top of the component. We'll need to wrap our react application with this HashRouter
component in order for routing to work.
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>
, document.getElementById('root'));
Now that our application is wrapped with HashRouter
, we can render our router any where in the app. Let's open ./src/App.js
and import ./src/routes.js
at the top of the file. Then, in the render
method, let's render our routes underneath the nav
element.
render() {
return (
<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>
)
}
./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter } from 'react-router-dom';
import './main.css';
import App from './App';
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>
, document.getElementById('root'));
./src/App.js
import React, { Component } from 'react';
import routes from './routes';
export default class App extends Component {
render() {
return (
<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>
)
}
}
In this step, we will be adding links to render our home and about views.
- Open
src/App.js
. - Import
Link
fromreact-router-dom
. - Locate the
div
elements with className of links.- Replace the
div
elements to beLink
components. - 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
to be Link
with 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 { Link } from 'react-router-dom';
import routes from './routes';
export default class App extends Component {
render() {
return (
<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>
)
}
}
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 params.
- Open
src/routes.js
. - Import the
ClassList
component to use as a route. - Create a
ClassList
route with the following properties:- Path:
/classlist/:class
- Component:ClassList
.
- Path:
- Open
src/Home/Home.js
. - Import
Link
fromreact-router-dom
. - Wrap each
button
element with aLink
component. - Each link should direct to the path
/classlist
with 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 { Switch, Route } from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';
import ClassList from './components/ClassList/ClassList';
export default (
<Switch>
<Route component={ Home } exact path="/" />
<Route component={ About } path="/about" />
<Route component={ ClassList } path="/classlist/:class" />
</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
.students
should default to an empty array.
- Create a
componentDidMount
method that makes aHTTP
request to thejson-server
:- Install
axios
andimport
it into the component. - The
json-server
API url ishttp://localhost:3005/students?class=
.- Class should equal
MATH1010
ORENG2010
ORBIO2020
depending on the route parameter. - Hint:
react-router-dom
passes down amatch
object on a component'sprops
.
- Class should equal
- Use the returned data from the API request to update the
students
array onstate
.
- Install
- Go into the
render
method of the component. map
over the students and return anh3
tag of the studentsfirst
andlast
name.- Remember react requires a unique
key
prop on mapped elements.
- Remember react requires a unique
- Undearneath the
h2
tag, render themapped
over students. - Update the
h1
tag to display the page's class name.- Hint:
react-router-dom
passes down amatch
object 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:3005/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:3005/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:3005/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() {
const students = this.state.students.map((student, i) => (
<h3 key={ i }>{ student.first_name } { student.last_name }</h3>
));
return (
<div className='box'>
<h1></h1>
<h2>ClassList:</h2>
</div>
)
}
We can then use { }
to render the h3
elements under the h2
element.
render() {
const students = this.state.students.map((student, i) => (
<h3 key={ i }>{ student.first_name } { student.last_name }</h3>
));
return (
<div className='box'>
<h1></h1>
<h2>ClassList:</h2>
{ students }
</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() {
super();
this.state = {
students: []
};
}
componentDidMount() {
axios.get(`http://localhost:3005/students?class=${ this.props.match.params.class }`).then( results => {
this.setState({
students: results.data
});
});
}
render() {
const students = this.state.students.map((student, i) => (
<h3 key={ i }>{ student.first_name } { student.last_name }</h3>
));
return (
<div className='box'>
<h1>{ this.props.match.params.class }</h1>
<h2>ClassList:</h2>
{ students }
</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
Student
component to use as a route. - Create a
Student
route with the following properties:- Path:
/student/:id
- Component:Student
.
- Path:
- Open
./src/components/ClassList/ClassList.js
. - Import
Link
fromreact-router-dom
. - Wrap the
h3
tag with aLink
component. - Assign the
to
prop for theLink
component to/student/:id
, whereid
should equal the student's ID.- Remember to move the unique
key
prop 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.
const students = this.state.students.map((student, i) => (
<Link to={`/student/${student.id}`} key={ i }>
<h3>{ student.first_name } { student.last_name }</h3>
</Link>
));
./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';
import ClassList from './components/ClassList/ClassList';
import Student from './components/Student/Student';
export default (
<Switch>
<Route component={ Home } exact path="/" />
<Route component={ About } path="/about" />
<Route component={ ClassList } path='/classlist/:class' />
<Route component={ Student } path='/student/:id' />
</Switch>
)
./src/components/ClassList/ClassList.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
export default class ClassList extends Component {
constructor() {
super();
this.state = {
students: []
};
}
componentDidMount() {
axios.get(`http://localhost:3005/students?class=${ this.props.match.params.class }`).then( results => {
this.setState({
students: results.data
});
});
}
render() {
const students = this.state.students.map((student, i) => (
<Link to={`/student/${student.id}`} key={ i }>
<h3>{ student.first_name } { student.last_name }</h3>
</Link>
));
return (
<div className='box'>
<h1>{ this.props.match.params.class }</h1>
<h2>ClassList:</h2>
{ students }
</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
constructor
method that initializes state with a property calledstudentInfo
.studentInfo
should default to an empty object.
- Create a
componentDidMount
method that makes anHTTP
request to thejson-server
:- Import
axios
into the component. - The
json-server
API url ishttp://localhost:3005/students/ID_GOES_HERE
.ID_GOES_HERE
should equal the student's id.- Hint:
react-router-dom
passes down amatch
object on a component'sprops
.
- Use the returned data from the API request to update the
studentInfo
object onstate
.
- Import
- Go into the
render
method of the component. - Underneath the
h1
tag, displaystudentInfo
properties off ofstate
:first_name
andlast_name
within anh1
tag.- The text
Grade:
followed by thegrade
property within anh3
tag. - The text
Email:
followed by theemail
property within anh3
tag.
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() {
super();
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:3005/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:3005/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() {
return axios.get(`http://localhost:3005/students/${this.props.match.params.id}`).then( results => {
this.setState({
studentInfo: results.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() {
super();
this.state = {
studentInfo: {}
};
}
componentDidMount() {
return axios.get(`http://localhost:3005/students/${this.props.match.params.id}`).then( results => {
this.setState({
studentInfo: results.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
Link
fromreact-router-dom
. - Inside the
div
with the classNamesubnav
, add 3h3
tags with the text:About
History
Contact
- Wrap each
h3
tag with aLink
component that links the following paths:- About -
/about
- History -
/about/history
- Contact -
/about/contact
- About -
- Give each of these
Link
components 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>
</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 provided a little bit of 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
Switch
andRoute
fromreact-router-dom
. - Import the
History
andContact
components. - Inside the
div
with the classNamebox
, add aSwitch
component. - Add 3 routes inside the
Switch
component:- The first two should route the
History
andContact
components.- Hint: the paths for these components are the same values used in the
to
prop on theLink
components.
- 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
render
prop 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>
-
- The first two should route the
./src/components/About/About.js
import React, { Component } from 'react';
import { Switch, Route, Link } 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 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.
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.