This is a solution to the Product feedback app challenge on Frontend Mentor.
About Frontend Mentor: Frotend Mentor offers a collection of realistic projects designed to enhance front-end development skills. Each challenge comes with detailed Figma designs, allowing us to build high-quality applications based on professional-grade mockups.
This project is a responsive and dynamic feedback application built using React.js and Bootstrap, designed to enhance user interaction and streamline the feedback process. It provides a seamless user experience with complex CRUD (Create, Read, Update, Delete) functionalities, allowing users to:
- View an optimal layout tailored to their device's screen size.
- Interact with hover states for all clickable elements on the page.
- Create, read, update, and delete product feedback requests.
- Receive form validations when submitting or editing feedback requests.
- Sort suggestions based on the number of upvotes and comments.
- Filter suggestions by category for easier navigation.
- Add comments and replies to existing product feedback requests.
- Upvote feedback requests to help prioritize suggestions.
This project utilizes a range of modern technologies and libraries to deliver a robust user experience:
- Semantic HTML5 for accessible and meaningful markup.
- CSS Custom Properties for easy theming and styling.
- Flexbox and CSS Grid for responsive and flexible layouts.
- React - JS library
- Bootstrap - For responsive design and pre-styled components.
- React Router DOM - For routing and navigation within the application, enabling multi-page functionality.
- Local Storage - For persisting data across sessions.
- Context API (useContext) for state management across components.
This really challenging project was my first multi-page React application, where I tackled complex CRUD operations and nested data management. I learned to effectively use React Router for navigation, understood the importance of creating deep copies to manage state updates, and utilized the useContext hook to manage global state, which streamlined my data flow and prevented prop drilling issues.
One of the significant challenges I faced during this project was learning to utilize React Router for multi-page navigation. Initially, I underestimated its complexity since React is optimized for single-page applications (SPAs). To effectively implement routing, I focused on three main concepts I learned:
1. Creating the Routers: I set up the router at the root level in main.jsx
using the createBrowserRouter
method, defining paths for each component within my application. This approach ensures that the router encapsulates all components, allowing them to access routing functionalities seamlessly. For example, I structured my routes as follows:
const router = createBrowserRouter([
{
path: "/",
element: <App />,
errorElement: <NotFoundPage />,
},
{
path: "/create-new-feedback",
element: <CreateNewFeedbackPage />,
errorElement: <NotFoundPage />,
},
{
path: "/feedback/:id",
element: <FeedbackDetail />,
errorElement: <NotFoundPage />,
},
{
path: "/edit-feedback/:id",
element: <EditFeedbackPage />,
errorElement: <NotFoundPage />,
},
{
path: "/roadmap",
element: <RoadmapPage />,
errorElement: <NotFoundPage />,
},
]);
2.Providing Router Context:: I utilized the to enable navigation between pages, effectively providing context for all components that require routing functionalities. This allowed for seamless transitions and a consistent user experience:
<RouterProvider router={router} />
3. Accessing Dynamic Parameters: I implemented dynamic routing to handle parameters in my links, using the useParams
hook to access specific data based on the route. In the FeedbackDetail.jsx
component, I utilized the useParams hook to extract the feedback ID from the URL, allowing me to display the corresponding feedback details:
const { id } = useParams();
const feedback = feedbackList.find((feedback) => feedback.id === parseInt(id));
Additionally, I included navigation links for returning to the previous page and editing feedback, enhancing user experience:
<Link to="/" className="d-flex semi-bold-grey-font-style">Go Back</Link>
<Link to={`/edit-feedback/${id}`}>Edit Feedback</Link>
I've learned to appreciate React's re-rendering behavior and the principle of immutability in state management. In my EditFeedbackPage component, I create a deep copy of the feedbackList before making any modifications. This ensures I follow the immutability principle and allows React to recognize changes, triggering re-renders when necessary.
For instance, I initialize the copy of the feedback list like this:
let copyOfFeedbackList = JSON.parse(JSON.stringify([...feedbackList]));
By doing so, I avoid directly modifying the original state. When deleting or updating feedback items, I use the setter function setFeedbackList
to ensure the component re-renders with the updated state:
copyOfFeedbackList = copyOfFeedbackList.filter(
(feedback) => feedback.id !== parseInt(cleanId)
);
setFeedbackList(copyOfFeedbackList);
In this project, I learned to use useContext hook for the first time to create global variables for managing feedback and the current user. This approach helps prevent issues with nested states in a larger application, ensuring that the state remains consistent across multiple pages and preventing prop drilling.
import { createContext } from "react";
import { createRoot } from "react-dom/client";
import useLocalStorage from "use-local-storage";
export const FeedbackListContext = createContext();
export const CurrentUserContext = createContext();
function Main() {
const [feedbackList, setFeedbackList] = useLocalStorage(
"feedbackList",
data.productRequests
);
const currentUser = data.currentUser;
return (
<CurrentUserContext.Provider value={{ currentUser }}>
<FeedbackListContext.Provider value={{ feedbackList, setFeedbackList }}>
{/* Router and app components go here */}
</FeedbackListContext.Provider>
</CurrentUserContext.Provider>
);
}
// Render the app
createRoot(document.getElementById("root")).render(<Main />);
-
I would love to turn this into a full-stack app. Right now, data is stored in a local JSON file, but I’m eager to connect it to an external database for storing, updating, and retrieving data.
-
I want to keep exploring how to build multi-page applications with React Router DOM and look into the most efficient ways and best practices for state management, especially when comparing useContext with other approaches.
- Portfolio - Nydia Subur
- Frontend Mentor - @nydiasubur
- Twitter/X - @nydiaSubur