Let's build a simple react application to implement the contextApi.
Create a new react app using the command:
npx create-react-app
The following dependencies will be required for the application:
react-router-dom: To help with routing between pages in our application
react-error-boundary: To help with implementing error boundary
tailwindcss: A low utility css framework to help with styling our application
To install these dependencies, use the command:
npm install react-router-dom react-error-boundary
Let's talk briefly about the project structure.
Components: This folder will hold all our reusable components like Navbar, Sidebar, etc.
Context: This folder will house our userContext configuration file.
Hooks: This will hold our custom hooks file.
Pages: The different pages in our application will be here.
Build the Navbar component
Inside the components folder, create a Navbar.jsx file and put this code.
import React, { useState, useContext } from 'react';
import { UserContext } from "../context/UserContext";
const Navbar = () => {
const [navbar, setNavbar] = useState(false);
const { isAuth, userInfo, logout } = useContext(UserContext);
const onLogout = (e) => {
e.preventDefault();
logout();
};
return (
<nav className="w-full bg-blue-800 shadow">
<div className="justify-between px-4 mx-auto lg:max-w-7xl md:items-center md:flex md:px-8">
<div>
<div className="flex items-center justify-between py-3 md:py-5 md:block">
<a href="/">
<h2 className="text-2xl font-bold text-white">AltSchool</h2>
</a>
<div className="md:hidden">
<button
className="p-2 text-gray-700 rounded-md outline-none focus:border-gray-400 focus:border"
onClick={() => setNavbar(!navbar)}
>
{navbar ? (
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6 text-white"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
)}
</button>
</div>
</div>
</div>
<div>
<div
className={`flex-1 justify-self-center pb-3 mt-8 md:block md:pb-0 md:mt-0 ${
navbar ? "block" : "hidden"
}`}
>
<ul className="items-center justify-center space-y-8 md:flex md:space-x-6 md:space-y-0">
<li className="text-white hover:text-indigo-200">
<a href="/">Home</a>
</li>
<li className="text-white hover:text-indigo-200">
<a href="/about">About</a>
</li>
<li className="text-white hover:text-indigo-200">
<a href="/contact">Contact</a>
</li>
</ul>
<div className="mt-3 space-y-2 lg:hidden md:hidden">
{!isAuth ? (
<a
href="/login"
className="inline-block w-full px-4 py-2 text-center text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
>
Sign in
</a>
) : (
<>
<p className="inline-block w-full px-4 py-2 text-center text-white">
Welcome, {userInfo?.username}
</p>
<button
onClick={onLogout}
className="inline-block w-full px-4 py-2 text-center text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
>
Logout
</button>
</>
)}
</div>
</div>
</div>
<div className="hidden space-x-2 md:flex">
{!isAuth ? (
<a
href="/login"
className="px-4 py-2 text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
>
Sign in
</a>
) : (
<>
<div class="overflow-hidden relative w-10 h-10 bg-gray-100 rounded-full dark:bg-gray-600">
<svg
class="absolute -left-1 w-12 h-12 text-gray-400"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
clip-rule="evenodd"
></path>
</svg>
</div>
<div className="px-4 py-2 text-white">
<p>Welcome, {userInfo?.username}</p>
</div>
<button
onClick={onLogout}
className="px-4 py-2 text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
>
Logout
</button>
</>
)}
</div>
</div>
</nav>
);
}
export default Navbar
Inside of the App.js file, import the navbar component.
import Navbar from "./components/Navbar.jsx";
const App = () => {
return (
<div className="App">
<Navbar />
</div>
)
}
Set up userContext
Inside the context folder, create a userContext.js file and put this code.
import React, { createContext, useState, useEffect } from "react";
const initialState = {
userInfo: null,
isAuth: false,
};
export const UserContext = createContext(initialState);
export const UserContextProvider = ({ children }) => {
const [userInfo, setUserInfo] = useState(null);
const [isAuth, setIsAuth] = useState(false);
useEffect(() => {
const storedUser = localStorage.getItem("user");
if (storedUser) {
setUserInfo(JSON.parse(storedUser));
setIsAuth(true);
}
}, []);
const login = (username, password) => {
setIsAuth(true);
setUserInfo({ username, password });
localStorage.setItem("user", JSON.stringify({username, password}));
localStorage.setItem("auth", "true");
};
const logout = () => {
fetch("/logout").then((res) => {
setIsAuth(false);
setUserInfo(null);
localStorage.removeItem("user");
localStorage.removeItem("auth");
});
};
const value = {
userInfo,
setUserInfo,
isAuth,
setIsAuth,
login,
logout,
};
return (
<UserContext.Provider value={value}> {children} </UserContext.Provider>
);
};
export default UserContext;
We start by setting our default states for the user which is null for userInfo and false for isAuth. We can then use the createContext by react to create the userContext using the default states. Next, we create the contextProvider which takes a props of children and also create functions to handle login and logout in our application. When a user logs in, we update the user state and also store the updated user state in the localStorage to persist the details i.e it does not return to default on refresh.
We should have something like this on our screen when we run our application.
Set up Routing
Now, let's set up our routing. First, we build all the pages and components needed like our dashboard, 404, Login, About and Contact pages and import them into out App.js file then we set up error boundary using the react-error-boundary package we installed earlier.
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { ErrorBoundary } from "react-error-boundary";
import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";
import NotFound from "./pages/404"
import Dashboard from "./pages/Dashboard";
import Navbar from "./components/Navbar.jsx";
import Login from './pages/Login';
const ErrorFallback = ({ error }) => {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre style={{ color: "red" }}>{error.message}</pre>
</div>
);
};
const App = () => {
return (
<div className="App">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Navbar />
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={<Dashboard />}/>
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
</ErrorBoundary>
</div>
);
};
export default App;
Set up Custom hook
We will now set a custom hook to get the user from the userContext and can be used anywhere in our code. Inside the hooks folder, create a getUser.js file and add this:
import { useContext } from 'react';
import { UserContext } from "../context/UserContext";
const useGetUser = () => {
const { userInfo, isAuth } = useContext(UserContext);
return {userInfo, isAuth};
};
export default useGetUser;
The useGetUser hook can now be used anywhere in our code to get the current logged in user.
Set up protected routing
The protected routing is used to protect our routes from being accessed by unauthorized users. So, a user has to be logged in before they can access a route that is protected.
import { Navigate } from "react-router-dom";
export const ProtectedRoute = ({ children }) => {
const isAuth = localStorage.getItem("auth");
if (!isAuth) {
// user is not authenticated
return <Navigate to="/login" />;
}
return children;
};
Import the protectedRoute into the App.js file and wrap the elements of the routes you want to protect like this:
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="/about"
element={
<ProtectedRoute>
<About />
</ProtectedRoute>
}
/>
<Route
path="/contact"
element={
<ProtectedRoute>
<Contact />
</ProtectedRoute>
}
The project should look something like this at the end:
The link to the complete project has been provided below as well as the project link and other important documentations for packages and dependencies used in this application. In case you encounter errors or have any questions, you can go through the links or ask me in the comment section.
Thank you for reading!
Github Repo
https://github.com/ChukwurahVictor/altschool_exam
Project live link
https://zippy-kheer-15c249.netlify.app/
Tailwind Docs
https://tailwindcss.com/docs/guides/create-react-app
Top comments (0)