This app is a fun way to learn React hooks, stateful componenets and props, etc.
The challenge is to build this app as a greenfield project, use a design spec, and review how to build an app with React.
This project was intended as a review of the basics of building with React but extended into Figma, Bootstrap.
- Figma: This was my first time actually creating a website design in Figma (as opposed to using a design spec in Figma). Figma is fairly intuitive so I just figured out as I went how to use some of the features e.g. creating components and grouping items.
- Bootstrap for React: I had recently gone thru a tutorial on Bootstrap and thought I would give it a try with React. Pretty quickly decided this was not the way to go. I was suprised by the amount of dependecies needed. Then I needed to import each Bootstrap feature I wanted to use. Since I am a novice with Bootstrap this felt like to much of a burden for this project's goal of reveiwing React.
- FontAwesome for React: This was a great opportunity to learn how to integrate FontAwesome icons into React.
- Componenet Tree: I have found that planning ahead and seeing the big picture of a coding project is important for success. I took the time to think about what each component's purpose was either being a presentational or container (stateful) component. When you are starting to code you can get bogged down in the details and lose sight of the overall goal. Having a component tree and plotting out what the user will need to do helps keep me on track.
- React: I found that I have retained quite a lot of the React basics I was taught in my Boot camp course. This time around I had a much deeper understanding of why React is so versitile and fast. I did not appreciate the efficiency of using the Virtual DOM for only updating the parts of the DOM that changed and not the whole thing.
-
started with mapping out which components would be container components and presentational components based on what the app should do and where the user data would flow.
-
designed the app in Figma Fresh Sticky Notes
-
built out all the JSX in the App.js file first
-
considered using Bootstrap for React since I just did a tutorial but after an initial start decided this was more trouble than it was worth for this project
-
Had issues with getting the image file's path correct. Initiall I had an images directory inside the src directory but I could not get the normal path of
./images/file.pngto work. I tried importing and naming the image and then usingsrc={image}but this didn't work either. Ended up putting the images in the Public folder instead of the src folder. Still not sure how happy I am with my choice but it worked 😊 -
When state components were added I had to remind myself that event handlers must be in the component that owns the state. You can not use setState outside of the state component. Event listeners can be in other components and can be passed as props or rather the reference of the event handler can be passed as props from the stateful component to the event listener component.
-
Remember to always copy state before updating it. This is important to not cause disruptions if something else is using that state. Also you never, never want to directly touch the DOM so copy, add, and then setState anew.
-
Dealing with specific value updates Shown below is the onType method that live in the App state component. This method takes three parameters, editMeID == id of note object changed & updatedKey == title or description that was updated in the note object & updatedValue == value of updated
- The 1st parameter that onType takes is the editMeID == id of note object changed. The event listener in the Note component will grab what id was changed. When on Type is passed to the Note component thru props this id will be available to change state in the App component.
- updatedNotes is where state is being copied but with some conditions to catch the changes. With
map()each note object in the notes array is checked ifnote.id does not equaleditMeIdit means that editMeId does not exist. The only way editMeId exist is if the event listener created it. So if no editMeId then no user input and the note object is returned unchangedelsethere must have been an event listener triggered and aneditMeIdcreated and available. Then there needs to be a differentiation of what was updated in that note objectiftheupdatedKeyis "title" then theupdatedValueneeds to be udated in the note objectelsetheupdatedKeymust be "description" and then thatupdatedValueshould be updated in the note object- then state can be updated with
this.setstate()and the user will see the values they are typing in the UI
onType = (editMeID, updatedKey, updatedValue) => { const updatedNotes = this.state.notes.map( note => { if (note.id !== editMeID) { return note; } else { if (updatedKey === "title" ) { note.title = updatedValue; return note; } else { note.description = updatedValue; return note; } } }) this.setState({notes: updatedNotes}); } -
add event listeners to Note component
- for user changes to the title
onChange={updateTitle} value={props.note.title}are used as attributes in the<input>for the title - write event listener (below) for title changes in the Notes component. The parameters used in the
onTypeevent handler are assigned values here. SoeditMeIdis created which begins the first of the if/else statements in theonTypemethod.
const Note = (props) => { const updateTitle = (e) => { const updatedValue = e.target.value; const editMeId = props.note.id; props.onType(editMeID, "title", updatedValue); } - for user changes to the title
-
add event handler for Search functionality
- in App create a
onSearchmethod that will be passed thru props to the Header component - first need to
map()over thestate.notesarray - need to turn all text strings (search string, the note's title and description) into the same case. Use a new consts to do this so nothing is permanently changed in the UI or in state.
const newSearchText = text.lowerCase();const title = note.title.toLowerCase();andconst description = note.description.toLowerCase();
- check each scenario that a user could make. I intially had this too simplified not taking into account if a user started to type in the search and then deleted the search. The first
ifstatement covers this and would reset the doesMatchSearch to true so they wouldn't lose a note unintentionally. - in the
elsestatement the search text is compared to the title and the description text using anincludes()method. - two new constants,
titleMatchanddescriptionMatchare created that return true for a match and false for no match - intitially I coded another set of if/else statements to cover the possible boolean outcomes for the new constants
if (titleMatch) { note.doesMatchSearch = true; } else if (descriptionMatch) { note.doesMatchSearch = true; } else { note.doesMatchSearch = false; } return note; - However there is a more consise way to do this by makeing another constant
hasMatchand set it equal to a boolean statement.const hasMatch = titleMatch || descriptionMatch. IftitleMatchor (||)descriptionMatchis false then hasMatch will be false but if one or both are true the it will be true. Now the doesMatchSearch can just be set to the value ofhasMatch.
- in App create a
onSearch = (text) => {
const newSearchText = text.toLowerCase();
const updatedNotes = this.state.notes.map( note => {
if (!newSearchText) {
note.doesMatchSearch = true;
return note;
} else {
const title = note.title.toLowerCase();
const description = note.description.toLowerCase();
const titleMatch = title.includes(newSearchText);
const descriptionMatch = description.includes(newSearchText);
const hasMatch = titleMatch || descriptionMatch;
note.doesMatchSearch = hasMatch;
return note;
}
})
}
-
make event listenser for Search function in the Header component
- use an
onChangelistener as an attribute in the search<input>element - add a constant called
updateSearchthat theonChangewill trigger - updateSearch will grab the target value from onChange and send it to the App component
const updateSearch = (e) => { const text = e.target.value; props.onSearch(text); } - use an
-
make an event listener and handler for Deleting a note.
- add
onClickto the<span>element in the note - write a very simple event listener. do not even need to catch a target.value or use e as a parameter
- in the App component create a
onDeletemethod to pass thru props to NoteList - use
filter()to keep only notes whosenote.id !== deleteMeIdfrom event listener- The
filter()function takes the current state and filters it against the ID passed in as an argument. If the ID does not match the ID of the task currently being iterated over, the task gets pushed into the new filtered array. If the ID does match, then it gets rejected by the filter function.
- The
- add
-
Added Lifecycle methods,
componentDidUpdate()andcomponentDidMount()to preserve the user's notes between sessions -
Added directions for the first time user in three notes. Imorted these notes from data.js file.
-
Wanted to let a user options for the search input field.
- added a
<form>element around the<input>so that when a user hit enter an onSubmit event listener would be trigged. - if an enter to holds the search constraints then will need a clear/return home button
- want an esc key to clear out the search text
- added a
installed font awesome's SVG core package
npm i --save @fortawesome/fontawesome-svg-core
installed the free versions of icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons
installed font awesome React component
npm i --save @fortawesome/react-fontawesome@latest
checked the package.json file and saw five new dependencies
imported individual icons as opposed to dynamic or global imports
import { faGithub, faLinkedin } from '@fortawesome/free-brands-svg-icons'
and the used this format for JSX
<FontAwesomeIcon icon={faEnvelope} />
<FontAwesomeIcon icon={faGithub} />
<FontAwesomeIcon icon={faLinkedin} />
</div>
** Warning! The fontawesome website gives all individual icon components with camelCase but it doesn't work in React when importing or calling icons. So faGitHub is what is in documentation for v6.4.0 but faGithub is what actually works. Not sure if this is an issue of different versions of fontawesome but seeing as I followed their directions and installed the lastest version of fontawesome @fortawesome/react-fontawesome@latest I am not sure.
- Website - Amy Spencer
- Frontend Mentor - @amyspencerproject
- Linkedin - amyspencercodes
