This project focus on:
Components
.State
.Refs
.Portals
.Create component folder.
Add ProjectSidebar
component
<aside>
element.Add NewProject
component for gathering all project data.
<input>
elements for Title, Description, and Due Date.Because they look the same, Add reusable Input
component.
<label>, <textarea>, and {...props}
.Add <ProjectSidebar/>
and <NewProject/>
to App.jsx.
For now the app looks like this:
NoProjectSelected
to display when no project is selected.<img>, <h2>, <p>, and <button>
elements.Button.jsx
component to use in multiple places, to avoid repeating the same code.children
. Button should collect all other ...props
that might be set, and then spread them on the button element. So later we can add for example the onChange
prop.NoProjectSelected
, but render NewProject
, or NoProjectSelected
, depending if the button got clicked or not. Therefore, add state
to manage this situation.property
; to indicate if project is new or not existence, and array
; for all the projects.handleStartAddProject
function to onStartAddProject
prop with <NoProjectSelected>
, and <ProjectsSidebar>
component (that contain button for adding new project).NoProjectSelected
, and ProjectsSidebar
), accept and destructure the prop onStartAddProject
from App (that connect to handleStartAddProject
function).onStartAddProject
prop, to onCilck
prop in Button.Next step is to collect the user input and validate them.
Validate user input & showing error Modal
via useImperativeHandle
;
<NewProject>
.Modal
component, and return built in <dialog>
.forwardRef
, extract the children
and buttonCaption
properties.children
(from props object), makes this component flexible. children
is pass to every component, so I can use Modal
as a wrapper to any content I want. And that content will be wrapped by the dialog
element.ref
.useImperativeHandle
, defining open
function. To expose a function that can be called from outside the component.showModal
method on the dialog element. I do it with useRef named dialog
.Modal
via tailwind - add styles to dialog, and form, and use the Button component.NewProject.jsx
.handleCancelAddProject
function for cancel the creation of new project. (Same code as handleStartAddProject
function, but selectedProjectId is set to undefined instaed of null).<NewProject>
with onCancel={handleCancelAddProject}
.NewProject.jsx
- accept onCancel
prop.onCancel
connected to cancel button.SelectedProject
component, return <header>
, <h1>
, and <button>
to delete this project, in one <div>
.project
prop, and in the rendered code output {project.title}, formated {project.dueDate}
, and {project.description}
.state
in App,jsx
.handleSelectProject
, with project id
as a value, and update the state of selectedProjectId
with the id
.handleSelectProject
pass to onSelectProject
prop.ProjectsSidebar
i should extract selectedProjectId
from the incoming props, and connect it to the button in this component, with onClick
prop with onSelectProject
as a value.selectedProjectId
, so I will use this prop that contain the id
of the project that was selected.project.id
I currently outputting is equal to the selectedProjectId
I get as a prop. Now i use {cssClasses}
as a value for {className}
prop.selectedProject componenet
and output it in the App
component, if a project was selected;content
variable to be equal to <SelectedProject project={selectedProject} />
, now I need to derive the selected project from the state
;const selectedProject = projectsState.projects.find((project) => project.id === projectsState.selectedProjectId);
.side bar
, the app failed. The reason is inside App
.onSelectProject
function to onSelectProject
prop, but in ProjectsSidebar.jsx
I use onSelectProject
prop that contain the function mention above, and I just pass this ahead to the button. And the button don't give the id
of the selected project. Therefore, more control of how it will be executed is needed;id
of the project that currently being rendered as a value to onSelectProject
, and therfore as a value to handleSelectProject
function.
```jsx
// So this:
onClick={onSelectProject}// Become this: onClick={() => onSelectProject(project.id)}

<br>
15. Handling Project Deletion:
* As before, I need to add a functn to `App.jsx`, a function which update the `state`, and remove the project from the array, and I need to pass that function to `SelectedProject` component.
* Add `handleDeleteProject` function, that update the state as the other function. Then filter out the selected project from the projects array.
* Pass a pointer to `handleDeleteProject` function with `onDelete` prop. So I can call this function through the `onDelete` prop from inside `SelectedProject` component.
* Inside `SelectedProject` I extract this newly added `onDelete` prop, and than connect it to the `delete button`, like this: ```onClick={onDelete}```.
16. Adding "Project Tasks" & A Tasks Component:
* Add new `Task` component with `h2`, **NEW TASK** placeholder (which will be replaced by input and button), `p`, `ul`, and styling.
* Then i use `<Tasks />` inside `SelctedProject` component.
* Now i replace **NEW TASK** placeholder with actual input, and a button.
* Add `NewTask` component. Including `input`, `button`, and styling including flex-box since I want the 2 elements next to each other.
17. Managing Tasks & Understanding Prop Drilling;
* Now I want to make sure that the added task will be shown. I can do it with a `ref`, but i will use `state` (`[enteredTask, setEnteredTask]`), inside `NewTask`.
* Add `handleChange` function that connect to the input field to update the entered task. This function get an `event` object as a parameter, that have `target` which is the input field, and value which is the `value` of the input field. (`event.target.value`).
* Then I add `onChange` prop to connect it to the `handleChange` function. (`<input onChange={handleChange}` />).
* To complete the two way binding, using `value={enteredTask}`, by feeding the entered task text into the input field. (`<input value={enteredTask}` />).
* Now I want to make sure that when pressing the `Add Task` button the entered task is added to a place it can be stored.
* It will be in `App`, as I already store there all the projects.
* So inside `App` I add `tasks: [],` to the `projectsState` object.
* Therefore I will add 2 new function for handling tasks: `handleAddTask()`, and `handleDeleteTask()`.
* Inside `NewTask` I add `handleClick()` function, as long as `onClick={handleClick}`.
* Inside `handleClick()`, I want to forward the entered value to the app component (`onAdd(enteredTask);`), and then I want to reset it back to an empty string (`setEnteredTask('');`).
* Now i need to get the entered task to the app component.
* I need to pass `handleAddTask()` to `NewTask` component. And `NewTask` is in `Task` component, and `Task` component is inside `SelectedProject` component. It's invovled prop drilling.
* So let's start: In `App`, add `handleAddTask` as a value to `SelectedProject`, in `onAddTask` prop. The same for `handleDeleteTask`.
* I will use this function in `Task`, that called from `SelectedProject`, that's why I write it there.
* On `SelectedProject` extract `onAddTask`, and `onDeleteTask`, in order to forward them inside to `Task`.
* In `Task` also destructing `onAddTask`, and `onDeleteTask` props that just being added.
* Then `onAddTask` will be forward to `<NewTask />`, and `onDeleteTask` will be use inside the current component.
* Inside `NewTask` destrcuturing the `onAdd` function from the props object.
* Inside `handleClick` in `NewTask`, forward the entered task to the onAdd function. (That will go to `Task`, which in the end is in `SelectedProject`, which than is in `App`)×¥
* Few more steps with `tasks`...

18. Clearing Tasks & Fixing Minor Bugs:
* Now i want to make sure that `clear` button on each task will perform his purpose. Add `handleDeleteTask` in `App`.
* Add `handleDeleteTask` function to `onDeleteTask` prop, as value to `<SelectedProject />`.
* Inside `Tasks` destruct `onDelete` prop, in order to connect it to `delete` button.
* On `button` add `onClick={onDelete}`, but wrap it in a function for full control of execution like this: `onClick={() => onDelete(task.id)}`.
<br>
---
<br>

