150. Module Introduction & Starting Project
This section is a hands-on practice project to apply React fundamentals and some advanced concepts by building a simple Project Management app. The app will let users create and view projects, add and clear tasks, and delete projects, combining concepts like components, state, refs, portals, and styling.
A starter project is provided in both local and CodeSandbox forms (equivalent). The local version requires installing dependencies and starting the dev server; CodeSandbox runs immediately. Tailwind CSS is already set up in the starter, so learners can use its utility classes (optionally referencing the Tailwind docs), but the priority is correct React functionality over styling. Learners are encouraged to attempt building the app independently before following the instructor’s step-by-step solution starting next lecture.
151. Adding a Projects Sidebar Component
You create a new components folder under src and add a
ProjectsSidebar.jsx component. That component returns an aside
containing an h2 (“Your Projects”) and a button (“+ Add Project”)
meant for starting a new project (project list will be added later).
Then, in App, you import and render ProjectsSidebar, replacing the
old h1 and wrapping the layout in a main element to hold the sidebar
and future project details. After confirming it renders, you note the
next step is styling.
152. Styling the Sidebar & Button with Tailwind CSS
The text describes adding Tailwind CSS classes to style a React app
layout and a ProjectsSidebar:
-
App component: add
h-screenso the layout fills the full viewport height (so the sidebar can later stretch full height), andmy-8for vertical margin. -
Sidebar (
aside) styling: set width tow-1/3, add paddingpx-8 py-16, dark backgroundbg-stone-900, light texttext-stone-50, use a fixed width on medium+ screens withmd:w-72, and round only the right corners withrounded-r-xl. -
Sidebar title (
h2) styling: add bottom margin,font-bold,uppercase, larger text on bigger screens, and adjust to a lighter gray color. -
Button styling: add padding,
text-xswithmd:text-base,rounded-md,bg-stone-700,text-stone-400, and hover styleshover:bg-stone-600 hover:text-stone-100.
It ends by noting the next step: make the button clickable to show a component/screen for entering details of a new project.
153. Adding the New Project Component & A Reusable Input Component
A new NewProject React component is created to collect data for a new
project. It renders a div with a menu containing Cancel and Save
buttons, and below that it shows inputs for Title, Description, and
Due Date (with Description using a textarea).
To avoid repeating similar JSX and Tailwind classes, a reusable Input
component is added (Input.jsx). It renders a label plus either an
input or textarea depending on a textarea prop, and it accepts a
label prop while spreading remaining props onto the rendered control
for full configurability.
NewProject then imports and uses Input three times with the
appropriate labels, enabling textarea for the description field.
Finally, NewProject is imported into the main App component and
displayed next to the sidebar by making the main container a flex layout
(flex with gap-8), ensuring the sidebar stretches to full height and
the new project inputs appear beside it.
154. Styling Buttons & Inputs with Tailwind CSS
-
Adds Tailwind styling to the “New Project” wrapper: sets a custom width using Tailwind’s arbitrary value syntax (
w-[35rem]) and pushes the section down withmt-16to align with the sidebar title. -
Styles the top menu bar in the New Project component using Flexbox (
flex,items-center,justify-end), spacing (gap-*) and vertical margin (my-4). -
Styles the two action buttons differently:
-
Cancel: subtle/flat look with dark gray text (
text-stone-800) and darker hover text. -
Save: prominent button with dark background, light text, hover background change, padding/margins, and rounded corners.
-
-
Updates
Input.jsxstyling:-
Wraps label + control in a vertical flex layout (
flex,flex-col) with spacing and vertical margins. -
Styles labels (
text-sm,font-bold,uppercase, gray color). -
Styles
textarea(and theninput) with full width, padding, bottom border, rounded corners, border/background/text colors, and focus states (remove default outline, change border color). -
Refactors repeated Tailwind classes into a shared
classesconstant applied to bothtextareaandinput.
-
-
Next planned steps: show fallback content before “Add Project” is clicked, open the form on click, then read user input values and create a new project.
155. Splitting Components to Split JSX & Tailwind Styles
-
Add a fallback UI that’s shown when no project is selected and the user is not currently adding a new project.
-
Create a new component
NoProjectSelectedthat renders:-
An image (
no-projects.pngimported from theassetsfolder), -
An
h2heading (“No Project Selected”), -
A paragraph prompting the user to select or create a project,
-
A “Create new project” button that should trigger showing the
NewProjectscreen.
-
-
Apply Tailwind styling:
-
Wrapper
div: top margin, centered text, width2/3. -
Image:
w-16 h-16 object-contain mx-auto. -
Heading:
text-xl font-bold text-stone-500 my-4. -
Text paragraph:
text-stone-400plus spacing.
-
-
Avoid duplicating button Tailwind classes by creating a reusable
Buttoncomponent:-
Move the button markup and classes from
ProjectsSidebarintoButton. -
Support flexible content via
children. -
Forward all additional props (
…props) onto the underlying<button>so it can receive handlers/attributes later.
-
-
Replace the existing buttons in both
ProjectsSidebarandNoProjectSelectedwith the newButtoncomponent (imported frombutton.jsx), removing repeatedclassNamedefinitions. -
Temporarily render
NoProjectSelectedinAppinstead ofNewProjectto preview it. -
Next step: implement conditional rendering in
Appso clicking the “Create new project” button switches the UI to theNewProjectcomponent.
156. Managing State to Switch Between Components
-
Use React state in the
Appcomponent to decide whether to showNoProjectSelectedorNewProject, since conditional rendering should be controlled where the decision is made. -
Add
useState(imported from React) and manage a single state object namedprojectsStatecontaining:-
selectedProjectId: initiallyundefined -
projects: initially an empty array
-
-
Define meaning for
selectedProjectId:-
undefined: no project selected and not adding a new one (show fallback screen) -
null: user is starting to add a new project (showNewProject) -
later: an actual project ID when a project is selected
-
-
Add
handleStartAddProjectinAppto setselectedProjectIdtonull, using the functionalsetProjectsState(prev => ({ …prev, selectedProjectId: null }))form to avoid losing existing state (like saved projects). -
Pass this handler down as
onStartAddProjectto bothNoProjectSelectedandProjectSidebar, and wire it to their “Add Project” buttons viaonClick. -
In
App, compute acontentvariable:-
if
selectedProjectId === null→ render<NewProject /> -
else if
selectedProjectId === undefined→ render<NoProjectSelected onStartAddProject={…} /> -
(later, this will be extended to render a selected project view)
-
-
Render
{content}next to the sidebar. Result: initial load shows the fallback screen; clicking either “Add Project” button switches to theNewProjectcomponent. -
Next step mentioned: collect input values to actually create and store a new project and make the other buttons work.
157. Collecting User Input with Refs & Forwarded Refs
- Goal
-
When clicking Save in
NewProject, collect input values, validate them (show an error modal if any are empty), and on success add the new project to state so it appears in the sidebar (validation + closing view will come next; this section implements the success path first).
-
Input value collection approach: Instead of controlled inputs with
onChange+ state, use refs because values are only needed on save.-
Import
useRefand create three refs:title,description,dueDate. -
Attach refs to the three input components.
-
-
Ref forwarding for a custom
Inputcomponent:-
In React 19+, passing a
refprop to a custom component works like a normal prop. -
In React 18 and earlier, you must wrap the
Inputcomponent withforwardRef:-
Import
forwardReffrom React. -
Wrap the component and accept
(props, ref). -
Assign
refto the underlying native<input>or<textarea>(depending on whethertextareaprop is used).
-
-
-
Save handling in
NewProject:-
Add
handleSaveand connect it to the Save button viaonClick. -
Inside
handleSave, read values viaref.current.valuefor all three fields. -
Call a prop callback (e.g.,
onAdd) and pass an object:{ title, description, dueDate }.
-
-
Lifting state to
Appto actually add projects:-
In
App, addhandleAddProject(projectData)to update the projects state using the functionalsetStateform. -
Preserve existing state and append a new project object to the
projectsarray. -
Add an
idto the new project (usingMath.random()as a simple stand-in).
-
-
Wiring:
-
Pass
handleAddProjecttoNewProjectasonAdd. -
Confirm via
console.logthat the projects array updates when saving. -
Note: double logs occur due to
StrictModecausing components to run twice in development.
-
-
Input improvements:
-
Set due date input
type="date"to get a date picker. -
Set title input
type="text"for completeness.
-
-
Next steps (not implemented yet in the described part):
-
Validate inputs and show an error modal if invalid.
-
Render newly added projects in the sidebar.
-
Close/hide the
NewProjectform after a successful save.
-
158. Handling Project Creation & Updating the UI
-
To close the “New Project” component after saving, the
Appcomponent must resetselectedProjectIdaway fromnull(becausenullis what keeps the new-project screen visible). -
In
handleAddProject, setselectedProjectIdtoundefinedso the UI returns to the fallback/overview screen after saving. (Alternative mentioned: set it to the newly created project’s id to auto-select it, but that’s deferred since selection/details aren’t implemented yet.) -
To show created projects in the sidebar:
-
Pass the projects array down to the sidebar via a
projectsprop (e.g.,projectsState.projects). -
In the sidebar component, map over
projectsto render an<li>per project, usingproject.idas thekey. -
Inside each list item, render a
<button>that displaysproject.title(preparing for future “select project” behavior).
-
-
Add Tailwind styling to the buttons (full width, left-aligned text, padding, rounded corners, margins, light text on dark background, hover styles) and add
mt-8to the<ul>for extra spacing above the list. -
Next steps planned: make the Cancel button work in the new-project form, and show a validation error modal if Save is clicked with missing input values.
159. Validating User Input & Showing an Error Modal via useImperativeHandle
-
Add input validation in the NewProject component: trim
title,description, anddateand treat them as invalid if any are empty strings. If invalid, show an error modal andreturnto preventonAddfrom running. -
Create a reusable Modal.jsx component built on the native
<dialog>element. -
Render the modal via a React portal into a
#modal-rootdiv usingcreatePortal(…)anddocument.getElementById('modal-root'). -
Make the modal controllable from outside without exposing internal
<dialog>details by using:-
forwardRef(for React 18 and compatibility; not required in React 19+) -
useImperativeHandleto expose anopen()method -
an internal
useRefto calldialogRef.current.showModal()
-
-
In NewProject, create
modalRef, attach it to<Modal ref={modalRef} />, and callmodalRef.current.open()when validation fails. -
Provide modal content in NewProject via
children(e.g., “Invalid input” + explanatory paragraphs). -
Add a close mechanism inside
Modal: a<form method="dialog">with a submit button to close the dialog. -
Improve reusability by accepting a
buttonCaptionprop to customize the close button text (e.g., “Close” or “Okay”). -
Result: modal appears on saving with empty/invalid fields; styling isn’t polished yet but functionality works.