React
Introduction
In this assignment, you will build a React application with multiple pages, reusable components, a counter, and theme switching. You will practice using key React Hooks (useState, useEffect, useContext) and implement routing using React Router.
This assignment is designed to give hands-on experience with real-world React patterns, including nested components and conditional rendering.
Learning Objectives
- Set up a React project and install dependencies.
- Implement reusable components and nested components.
- Use React Hooks (
useState,useEffect,useContext). - Implement routing with React Router.
- Manage global state with
useContextandcreateContext. - Implement conditional rendering and dynamic messages.
Folder Structure
src/
├── App.js
├── index.js
├── components/
│ └── Navbar/
│ └── Navbar.js
├── pages/
│ ├── Home/
│ │ └── Home.js
│ └── CounterPage/
│ ├── CounterPage.js
│ ├── CounterDisplay.js
│ └── CounterControls.js
└─ ─ context/
└── ThemeContext.js
What's the difference between components/ and pages/?
components/- Reusable UI pieces that can appear on multiple pages. For example, the Navbar shows up on every page, so it goes incomponents/. Think of these as building blocks.pages/- Full pages that users navigate to via routes (like/homeor/counter). Each page is typically made up of multiple components.context/- Contains React Context files for sharing state globally across your app (like theme).
This organization keeps your code clean and makes it easy to find things!
The src/ folder is where you'll write all your code. When you create your React app, you'll also see some other important files and folders outside of src/ that are used for setup and dependencies:
node_modules/- Contains all the packages/dependencies your project needs. This folder is generated from the packages inpackage.jsonwhen you runnpm install. It should NOT be uploaded to GitHub or Gradescope.package.json- Lists your project's dependencies and scripts (likenpm start). Runningnpm install <package>will add the package to bothpackage.jsonandnode_modules/. After installing, you can import and use functions, components, and other code from that package in your project. Packages come from the npm registry, a huge collection of open-source code that developers share.package-lock.json- Locks the exact versions of dependencies. Helps ensure consistent installs.public/- Contains static files likeindex.html(the single HTML page your React app mounts to) and images..gitignore- Tells Git which files to ignore (likenode_modules/).
Part 1: Setup
Description: You will create a new React project and start the development server.
-
Create Project
npx create-react-app my-app
cd my-appnpx vs npmYou might notice we use
npxhere instead ofnpm. What's the difference?-
npminstalls packages to your project (or globally) so you can use them repeatedly. We used this in lecture. -
npxruns a package directly without installing it permanently. It's useful for one-time commands likecreate-react-appthat you only need to run once to set up a project.
Both come with Node.js, so you have access to both!
-
-
Start Project
npm startOpen
http://localhost:3000in your browser.
Part 2: Pages and Components Skeleton
Description: You will create the basic skeleton of your application with all necessary pages and components. This provides the foundation to add logic, styling, and hooks later. Take some time to understand how all these files connect to each other—notice how components are imported and used across different files, and how data flows between them!
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
// TODO: Wrap <App /> with <BrowserRouter>
// TODO: Wrap <App /> with <ThemeProvider>
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
App.js
import React from "react";
import Navbar from "./components/Navbar/Navbar";
import Home from "./pages/Home/Home";
import CounterPage from "./pages/CounterPage/CounterPage";
// TODO: Add Routes for Home and CounterPage
function App() {
return (
<div>
<Navbar />
{/* Add <Routes> and <Route> components here */}
</div>
);
}
export default App;
components/Navbar/Navbar.js
import React from "react";
// TODO: Add navigation links to Home and CounterPage
// TODO: Add a theme toggle button
// TODO: Use useContext to access theme and toggle function
// TODO: Apply class "navbar-light" or "navbar-dark" using ternary operator based on theme
// TODO: Create Navbar.css with styles for navbar-light and navbar-dark
function Navbar() {
return <nav></nav>;
}
export default Navbar;
pages/Home/Home.js
import React from "react";
// TODO: Add content for Home page (welcome message or description)
// TODO: Use useContext to access theme and style accordingly
function Home() {
return <div></div>;
}
export default Home;
pages/CounterPage/CounterPage.js
import React from "react";
import CounterDisplay from "./CounterDisplay";
import CounterControls from "./CounterControls";
// TODO: Use useState to manage counter
// TODO: Use useEffect to show a message when counter > 5
// TODO: Use useEffect to reset counter to 0 if it becomes negative
// TODO: Pass counter value to CounterDisplay as a prop
// TODO: Pass increment/decrement functions to CounterControls as props
function CounterPage() {
return (
<div>
<CounterDisplay />
<CounterControls />
{/* TODO: Show a message when counter > 5 */}
</div>
);
}
export default CounterPage;
pages/CounterPage/CounterDisplay.js
import React from "react";
// TODO: Receive counter value as prop and display it
// Description: Shows the current value of the counter
function CounterDisplay() {
return <div></div>;
}
export default CounterDisplay;
pages/CounterPage/CounterControls.js
import React from "react";
// TODO: Receive increment/decrement functions as props
// TODO: Add buttons for Increase and Decrease
// Description: Provides buttons to change the counter value
function CounterControls() {
return <div></div>;
}
export default CounterControls;
context/ThemeContext.js
import React, { createContext } from "react";
// TODO: Use useState to create state for theme (light/dark)
// TODO: Create a toggle function to switch between light and dark
// TODO: Pass theme and toggle function in the Provider value
export const ThemeContext = createContext();
export function ThemeProvider({ children }) {
return <ThemeContext.Provider value={{}}>{children}</ThemeContext.Provider>;
}
Part 3: Routing
Description: You will implement navigation in your app so users can move between Home and CounterPage using React Router.
First, install the react-router-dom package:
npm install react-router-dom
Check that react-router-dom appears in your package.json dependencies. Now you can import and use components from this package:
- In
index.js:import { BrowserRouter } from "react-router-dom"; - In
App.js:import { Routes, Route } from "react-router-dom"; - In
Navbar.js:import { Link } from "react-router-dom";
Here's what each component does:
<BrowserRouter>- Wraps your entire app and enables routing. It keeps track of the current URL and lets your app respond to URL changes.<Routes>- A container for all your<Route>components. It looks at the current URL and renders the matching route.<Route>- Defines a single route. You specify apath(like/home) and anelement(the component to render when the URL matches).<Link>- Like an<a>tag but for React Router. It lets users navigate without refreshing the page.
You can actually see the code for react-router-dom inside your node_modules/ folder (recall that this folder contains all the packages your project depends on). However, it's probably too complex to understand. When using a package, you should always refer to its documentation or its page on the npm registry to learn how to use it.
Then:
- Wrap
<App />with<BrowserRouter>inindex.js. - Use
<Routes>and<Route>inApp.jsfor Home (/home) and CounterPage (/counter). - Add navigation links (using
<Link>) in Navbar to Home and CounterPage. - Optional: add extra pages for practice.
Part 4: Counter Page Logic
Description: You will add functionality to the counter page so users can increase/decrease the value and see messages based on the counter.
- Use
useStatein CounterPage to manage the counter value. - Use
useEffectto:- Show a message when counter > 5.
- Reset counter to 0 if it becomes negative.
- Pass the counter value as a prop to CounterDisplay.
- Pass increment/decrement functions from CounterPage to CounterControls as props.
- Add Increase and Decrease buttons in CounterControls.
Conditional Rendering with &&
To show the message only when counter > 5, use the && operator for conditional rendering:
{counter > 5 && <p>You passed 5!</p>}
How does && work? In JavaScript, && returns the second value if the first is true, otherwise it returns the first value. React won't render false, so:
- If
counter > 5isfalse→ nothing renders - If
counter > 5istrue→ the<p>element renders
This is super useful in React for showing/hiding elements based on conditions without needing a full if-else statement!
Part 5: Theme Context
Description: Implement global light/dark theme using useContext and createContext so users can toggle the theme across all pages.
- Use
useStatein ThemeContext to manage the theme state ("light"or"dark"). - Create a toggle function to switch between light and dark.
- Pass both theme and toggle function in the ThemeProvider value.
- Wrap
<App />with<ThemeProvider>inindex.js. - Add a toggle button in Navbar to switch themes.
- Use
useContextto access theme in the Navbar component.
Required: Style the Navbar Based on Theme
You must style the Navbar differently based on the theme. Use these exact specifications:
In Navbar.js, apply a dynamic class to the <nav> element:
<nav className={theme === "dark" ? "navbar navbar-dark" : "navbar navbar-light"}>
In Navbar.css, add these exact styles:
.navbar-light {
background-color: #ffffff;
color: #333333;
}
.navbar-light .nav-link {
color: #333333;
}
.navbar-dark {
background-color: #333333;
color: #ffffff;
}
.navbar-dark .nav-link {
color: #ffffff;
}
Light mode: white background (#ffffff), dark text (#333333)
Dark mode: dark background (#333333), white text (#ffffff)
The autograder will check that your Navbar changes appearance when the theme is toggled. Make sure you use the exact class names navbar-light and navbar-dark.
Part 6: Submission Instructions
Description: Guidelines to submit your project so it can be graded properly.
-
Include:
src/folderpublic/folderpackage.jsonpackage-lock.json.gitignore
-
Do not include
node_modules/. -
Use
.gitignoreto ignorenode_modules:/node_modules -
Zip the project and upload to Gradescope.