https://github.com/romashka-dev/react-fundamentals
ReactJS Fundamentals: Amazon Best Sellers app
https://github.com/romashka-dev/react-fundamentals
destructuring mapping-data prop-drilling props-react react react-components
Last synced: 9 months ago
JSON representation
ReactJS Fundamentals: Amazon Best Sellers app
- Host: GitHub
- URL: https://github.com/romashka-dev/react-fundamentals
- Owner: romashka-dev
- Created: 2024-12-04T13:54:01.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-12-11T14:58:29.000Z (about 1 year ago)
- Last Synced: 2025-02-14T06:36:35.071Z (10 months ago)
- Topics: destructuring, mapping-data, prop-drilling, props-react, react, react-components
- Language: JavaScript
- Homepage: https://reactjs-amazon-best-sellers.netlify.app/
- Size: 232 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# React Fundamentals
[](https://app.netlify.com/sites/reactjs-amazon-best-sellers/deploys)
#### React Course: Complete React, Next.js & TypeScript Projects Course 2024 (Created by John Smilga)
[Link](https://www.udemy.com/course/react-tutorial-and-projects-course/?referralCode=FEE6A921AF07E2563CEF)
#### Folder Structure
- node_modules
Contains all dependencies required by the app. Main dependencies also listed in package.json
- public
Contains static assets including index.html (page template)
- index.html
- title
- fonts
- css
- favicon
- id="root" - our entire app
- src
In simplest form it's the brain of our app. This is where we will do all of our work. src/index.js is the JavaScript entry point.
- .gitignore
Specifies which files source control (Git) should ignore
- package.json
Every Node.js project has a package.json and it contains info about our project, for example list of dependencies and scripts
- package-lock.json
A snapshot of the entire dependency tree
- README
The markdown file where you can share more info about the project for example build instructions and summary
- zoom 175%
#### Remove Boilerplate
- remove src folder
- create src folder
- create index.js inside src
- toggle sidebar CMD + B
- shortcuts settings/keyboard shortcuts
#### First Component
```js
function Greeting() {
return
My First Component
}
// arrow function also works
const Greeting = () => {
return
My First Component
}
```
- starts with capital letter
- must return JSX (html)
- always close tag
##### Typical Component
```js
// imports or logic
const Greeting = () => {
return
My First Component
}
export default Greeting
```
##### Root Component (only one)
index.js
```js
import React from 'react'
import ReactDOM from 'react-dom/client'
function Greeting() {
return
My First Component
}
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render()
```
#### Possible Bug
If for some reason you still have this error in the terminal
```
Module not found: Error: Can't resolve 'path/index.js'
```
Just restart the server
- CTRL + C (stop the server)
- "npm start" (start the dev server)
#### Extensions and settings.json
- Auto Rename Tag
- Highlight Matching Tag
- customize in settings.json
- Prettier
- format on save
- format on paste
- Default Formatter (Prettier - Code formatter)
settings.json
```json
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.singleQuote": true,
"prettier.semi": false,
```
- Emmet
settings.json
```json
"emmet.includeLanguages": {
"javascript": "javascriptreact"
},
```
- ES7 Snippets
- rafce (arrow func with export)
- rfce (regular func with export )
- same as the file name
- react auto import
- uncheck
- React Snippets › Settings: Import React On Top
#### First Component in Detail
- capital letter
- must return something
- JSX syntax (return html)
- to make our lives easier
- calling function under the hood
index.js
```js
const Greeting = () => {
return React.createElement('h2', {}, 'hello world')
}
```
```js
function Greeting() {
return (
hello world
)
}
const Greeting = () => {
return React.createElement(
'div',
{},
React.createElement('h2', {}, 'hello world')
)
}
```
#### JSX Rules
- return single element (one parent element)
- semantics section/article
- Fragment - let's us group elements without adding extra nodes
```js
return ...rest of the return
// shorthand
return <>...rest of the return>
```
- camelCase property naming convention
```js
return (
click me
Name
)
// in html
click me
Name
```
- className instead of class
```js
return
hello
```
- close every element
```js
return
// or
return
```
- formatting
- opening tag in the same line as return or ()
```js
function Greeting() {
return (
<>
hello world
>
)
}
```
#### Nest Components
```js
function Greeting() {
return (
)
}
const Person = () =>
john doe
const Message = () => {
return this is my message
}
```
#### React Developer Tools
- top right corner
- more tools/extensions
- open chrome web store
#### Book List
- setup structure
```js
import React from 'react'
import ReactDOM from 'react-dom/client'
function BookList() {
return (
)
}
const Book = () => {
return (
)
}
const Image = () =>
image placeholder
const Title = () => {
return Book Title
}
const Author = () => Author
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render()
```
- in search engine type - 'amazon best selling books'
[Amazon Best Sellers](https://www.amazon.com/Best-Sellers-Books/zgbs/books/)
- DON'T NEED TO BUY ANYTHING !!!
- NOT AN AFFILIATE LINK !!!!
- choose a book
- copy image, title and author
```js
import React from 'react'
import ReactDOM from 'react-dom/client'
function BookList() {
return (
)
}
const Book = () => {
return (
)
}
const Image = () => (
)
const Title = () => {
return
Interesting Facts For Curious Minds
}
const Author = () => Jordan Moore
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render()
```
#### CSS
- create index.css in src
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background: #f1f5f8;
color: #222;
}
```
- import file and add classes
```js
import './index.css'
function BookList() {
return (
)
}
const Book = () => {
return (
)
}
```
- complete css
```css
.booklist {
width: 90vw;
max-width: 1170px;
margin: 5rem auto;
display: grid;
gap: 2rem;
}
@media screen and (min-width: 768px) {
.booklist {
grid-template-columns: repeat(3, 1fr);
}
}
.book {
background: #fff;
border-radius: 1rem;
padding: 2rem;
text-align: center;
}
.book img {
width: 100%;
object-fit: cover;
}
.book h2 {
margin-top: 1rem;
font-size: 1rem;
}
```
#### Local Images (Public Folder)
- Optional Video !!!
- external images (hosted on different server) - just need an url
- local images (public folder) - less performant
- local images (src folder) - better solution for assets,
since under the hood they get optimized.
- save image (Save Image As....)
- create images folder in public
- copy/paste image
- rename (optional)
- replace url in the src - './images/imageName.extension'
- './' because assets are on the same server
```js
const Image = () => (
)
```
- whatever assets we place in public - instantly available
- domain(localhost)/asset
#### JSX - CSS (inline styles)
- style prop
- {} in JSX means going back to JS Land
- value is an object with key/value pairs - capitalized and with ''
```js
const Author = () => (
Jordan Moore
)
```
- css rules still apply (inline vs external css)
```css
.book h4 {
/* won't work */
color: red;
/* will work */
letter-spacing: 2px;
}
```
- external libraries use inline css,
so if you want to make some changes,
reference the library docs and elements tab
- alternative option
```js
const Author = () => {
const inlineHeadingStyles = {
color: '#617d98',
fontSize: '0.75rem',
marginTop: '0.5rem',
}
return
Jordan Moore
}
```
- FOR THE MOST PART, MULTIPLE APPROACHES AVAILABLE !!!
- AS LONG AS THE RESULT IS THE SAME, REALLY COMES DOWN TO PREFERENCE !!!!
#### JSX - Javascript
- refactor to single book component (personal preference)
- remove inline css
```js
const Book = () => {
return (
Interesting Facts For Curious Minds
Jordan Moore
)
}
```
```css
.book h4 {
color: #617d98;
font-size: 0.75rem;
margin-top: 0.5rem;
letter-spacing: 2px;
}
```
- {} in JSX means going back to JS Land
- value inside must be an expression (return value),
can't be a statement
```js
const author = 'Jordan Moore'
const Book = () => {
const title = 'Interesting Facts For Curious Mindssssss'
return (
{title}
{author.toUpperCase()}
{/* {let x = 6}
*/}
{6 + 6}
)
}
```
- toggle line comment Edit/Toggle Line Comment
#### Props - Initial Setup
- refactor/clean up
```js
const author = 'Jordan Moore'
const title = 'Interesting Facts For Curious Minds'
const img = './images/book-1.jpg'
function BookList() {
return (
)
}
const Book = () => {
return (
{title}
{author}
)
}
```
```js
// parameters
const someFunc = (param1, param2) => {
console.log(param1, param2)
}
// arguments
someFunc('job', 'developer')
```
```js
const Book = (props) => {
console.log(props)
return (
{title}
{author}
{console.log(props)}
)
}
```
- props object, convention to call props, 'shakeAndBake' is an excellent alternative
- pass as key/value pairs
- if the prop exists it will return value, otherwise no value
```js
function BookList() {
return (
)
}
const Book = (props) => {
console.log(props)
return (
{title}
{author}
{props.job}
{props.title}
{props.number}
)
}
```
```js
function BookList() {
return (
)
}
const Book = (props) => {
console.log(props)
return (
{props.title}
{props.author}
)
}
```
#### Props - Somewhat Dynamic Setup
- setup an object
- refactor vars to properties
- copy/paste and rename
- get values for second book
- setup props
```js
const firstBook = {
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: './images/book-1.jpg',
}
const secondBook = {
author: 'James Clear',
title: 'Atomic Habits',
img: 'https://images-na.ssl-images-amazon.com/images/I/81wgcld4wxL._AC_UL900_SR900,600_.jpg',
}
function BookList() {
return (
)
}
const Book = (props) => {
console.log(props)
return (
{props.title}
{props.author}
)
}
```
#### Access Props - Multiple Approaches
- there is no right or wrong - again preference !!!
- Destructuring (object)
[JS Nuggets - Destructuring (object)](https://www.youtube.com/watch?v=i4vhNKihfto&list=PLnHJACx3NwAfRUcuKaYhZ6T5NRIpzgNGJ&index=8&t=1s)
- destructuring in Vanilla JS
- saves time/typing
- pull out the properties
- don't need to reference object anymore
```js
const someObject = {
name: 'john',
job: 'developer',
location: 'florida',
}
console.log(someObject.name)
const { name, job } = someObject
console.log(job)
```
- no need for all the props.propName
- destructure inside component
```js
const Book = (props) => {
const { img, title, author } = props
return (
{title}
{author}
)
}
```
- destructure in function parameters (in our case props)
- if you have console.log(props) - it won't be defined
```js
const Book = ({ img, title, author }) => {
return (
{title}
{author}
)
}
```
#### Children Prop
- everything we render between component tags
- during the course we will mostly use it Context API
- special prop, has to be "children"
- can place anywhere in JSX
```js
function BookList() {
return (
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Itaque
repudiandae inventore eos qui animi sed iusto alias eius ea sapiente.
click me
)
}
const Book = ({ img, title, author, children }) => {
// rest of the logic
}
const Book = (props) => {
const { img, title, author, children } = props
console.log(props)
return (
{title}
{author}
{children}
)
}
```
- optional
```css
@media screen and (min-width: 768px) {
.booklist {
grid-template-columns: repeat(3, 1fr);
align-items: start;
}
}
.book p {
margin: 1rem 0 0.5rem;
}
```
#### Simple List
- [Javascript Nuggets - Map ](https://www.youtube.com/watch?v=80KX6aD9R7M&list=PLnHJACx3NwAfRUcuKaYhZ6T5NRIpzgNGJ&index=1)
- refactor
```js
const books = [
{
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: './images/book-1.jpg',
},
{
author: 'James Clear',
title: 'Atomic Habits',
img: 'https://images-na.ssl-images-amazon.com/images/I/81wgcld4wxL._AC_UL900_SR900,600_.jpg',
},
]
function BookList() {
return
}
const Book = (props) => {
const { img, title, author } = props
return (
{title}
{author}
)
}
```
- can't render objects in React
```js
function BookList() {
return {books}
}
```
- map - creates a new array from calling a function for every array element.
```js
const names = ['john', 'peter', 'susan']
const newNames = names.map((name) => {
console.log(name)
return
{name}
})
function BookList() {
return {newNames}
}
```
#### Proper List
- remove names and newNames
```js
function BookList() {
return (
{books.map((book) => {
console.log(book)
// return 'hello';
return (
{book.title}
)
})}
)
}
```
- render component
- pass properties one by one
```js
function BookList() {
return (
{books.map((book) => {
console.log(book)
const { img, title, author } = book
return
})}
)
}
```
#### Key Prop
- typically it's going to be id
```js
const books = [
{
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: './images/book-1.jpg',
id: 1,
},
{
author: 'James Clear',
title: 'Atomic Habits',
img: 'https://images-na.ssl-images-amazon.com/images/I/81wgcld4wxL._AC_UL900_SR900,600_.jpg',
id: 2,
},
]
function BookList() {
return (
{books.map((book) => {
console.log(book)
const { img, title, author, id } = book
return
})}
)
}
```
- you will see index,but it's not advised if the list is changing
```js
function BookList() {
return (
{books.map((book, index) => {
console.log(book)
const { img, title, author, id } = book
return
})}
)
}
```
#### Pass The Entire Object
- render component
- pass entire object
- Destructuring (object)
[JS Nuggets - Destructuring (object)](https://www.youtube.com/watch?v=i4vhNKihfto&list=PLnHJACx3NwAfRUcuKaYhZ6T5NRIpzgNGJ&index=8&t=1s)
```js
function BookList() {
return (
{books.map((book) => {
console.log(book)
const { img, title, author } = book
return
})}
)
}
const Book = (props) => {
const { img, title, author } = props.book
return (
{title}
{author}
)
}
```
- alternative
```js
const Book = ({ book: { img, title, author } }) => {
return (
{title}
{author}
)
}
```
#### My Personal Preference
- utilize spread operator (...) - copy values
- Spread Operator
- [JS Nuggets - Spread Operator](https://www.youtube.com/watch?v=4Zyr5a3m0Fc&list=PLnHJACx3NwAfRUcuKaYhZ6T5NRIpzgNGJ&index=10)
```js
const friends = ['john', 'peter', 'anna']
const newFriends = [...friends, 'susan']
console.log(friends)
console.log(newFriends)
const someObject = {
name: 'john',
job: 'developer',
}
// COPY NOT A REFERENCE !!!!
const newObject = { ...someObject, location: 'florida' }
console.log(someObject)
console.log(newObject)
```
```js
function BookList() {
return (
{books.map((book) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author } = props
return (
{title}
{author}
)
}
const Book = ({ img, title, author }) => {
// rest of the code
}
```
#### Events - Fundamentals
- Vanilla JS
```js
const btn = document.getElementById('btn')
btn.addEventListener('click', function (e) {
// access event object
// do something when event fires
})
```
- similar approach
- element, event, function
- again camelCase
```js
const EventExamples = () => {
const handleButtonClick = () => {
alert('handle button click')
}
return (
click me
)
}
```
- [React Events](https://reactjs.org/docs/events.html)
- no need to memorize them(idea is the same)
- most common
- onClick (click events)
- onSubmit (submit form )
- onChange (input change )
```js
function BookList() {
return (
{books.map((book) => {
return
})}
)
}
const EventExamples = () => {
const handleFormInput = () => {
console.log('handle form input')
}
const handleButtonClick = () => {
alert('handle button click')
}
return (
Typical Form
click me
)
}
```
#### Event Object and Form Submission
```js
const EventExamples = () => {
const handleFormInput = (e) => {
console.log(e)
// e.target - element
console.log(`Input Name : ${e.target.name}`)
console.log(`Input Value : ${e.target.value}`)
// console.log('handle form input');
}
const handleButtonClick = () => {
alert('handle button click')
}
const handleFormSubmission = (e) => {
e.preventDefault()
console.log('form submitted')
}
return (
{/* add onSubmit Event Handler */}
Typical Form
{/* add button with type='submit' */}
submit form
click me
)
}
```
- alternative approach
```js
submit form
```
#### Mind Grenade
- alternative approach
- pass anonymous function (in this case arrow function)
- one liner - less code
```js
const EventExamples = () => {
return (
console.log('hello there')}>click me
)
}
```
- also can access event object
```js
const EventExamples = () => {
return (
Typical Form
console.log(e.target.value)}
style={{ margin: '1rem 0' }}
/>
console.log('you clicked me')}>click me
)
}
```
#### Mind Grenade #2
- remove EventsExamples
- components are independent by default
```js
function BookList() {
return (
{books.map((book) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author } = props
const displayTitle = () => {
console.log(title)
}
return (
{title}
display title
{author}
)
}
```
- remove button
#### Prop Drilling
- react data flow - can only pass props down
- alternatives Context API, redux, other state libraries
```js
function BookList() {
const someValue = 'shakeAndBake'
const displayValue = () => {
console.log(someValue)
}
return (
{books.map((book) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author, displayValue } = props
return (
{title}
click me
{author}
)
}
```
#### More Complex Example
- initial setup
- create getBook function in booklist
- accepts id as an argument and finds the book
- [Javascript Nuggets - Filter and Find](https://www.youtube.com/watch?v=KeYxsev737s&list=PLnHJACx3NwAfRUcuKaYhZ6T5NRIpzgNGJ&index=4)
- pass the function down to Book Component and invoke on the button click
- in the Book Component destructure id and function
- invoke the function when user clicks the button, pass the id
- the goal : you should see the same book in the console
```js
const BookList = () => {
const getBook = (id) => {
const book = books.find((book) => book.id === id)
console.log(book)
}
return (
{books.map((book) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author, getBook, id } = props
// console.log(props);
return (
{title}
{/* this is not going to work */}
display title
{author}
)
}
```
- two fixes
- first option - setup wrapper
```js
const Book = (props) => {
const { img, title, author, getBook, id } = props
// console.log(props);
const getSingleBook = () => {
getBook(id)
}
return (
{title}
display title
{author}
)
}
```
- two fixes
- second option - wrap in the anonymous arrow function
```js
const Book = (props) => {
const { img, title, author, getBook, id } = props
// console.log(props);
const getSingleBook = () => {
getBook(id)
}
return (
{title}
getBook(id)}>display title
{author}
)
}
```
#### Import and Export Statements
- remove all getBook code
```js
function BookList() {
return (
{books.map((book) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author } = props
return (
{title}
{author}
)
}
```
- setup two files in src books.js and Book.js
- cut books array from index.js
- add to books.js
books.js
```js
const books = [
{
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: './images/book-1.jpg',
id: 1,
},
{
author: 'James Clear',
title: 'Atomic Habits',
img: 'https://images-na.ssl-images-amazon.com/images/I/81wgcld4wxL._AC_UL900_SR900,600_.jpg',
id: 2,
},
]
```
- two flavors named and default exports
- with named exports names MUST match
- with default exports,can rename but only one per file
- named export
```js
export const books = [
{
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: './images/book-1.jpg',
id: 1,
},
{
author: 'James Clear',
title: 'Atomic Habits',
img: 'https://images-na.ssl-images-amazon.com/images/I/81wgcld4wxL._AC_UL900_SR900,600_.jpg',
id: 2,
},
]
```
index.js
```js
import { books } from './books'
```
- default export
```js
const Book = (props) => {
const { img, title, author } = props
return (
{title}
{author}
)
}
export default Book
```
index.js
```js
import Book from './Book'
```
#### Local Images (src folder)
- better performance because optimized
- add one more book to array
- download all three images (rename)
- setup images folder in the src
- import all three images in the books.js
- set image property equal to import
- and yes each image requires new import
```js
import img1 from './images/book-1.jpg'
import img2 from './images/book-2.jpg'
import img3 from './images/book-3.jpg'
export const books = [
{
author: 'Jordan Moore',
title: 'Interesting Facts For Curious Minds',
img: img1,
id: 1,
},
{
author: 'James Clear',
title: 'Atomic Habits',
img: img2,
id: 2,
},
{
author: 'Stephen King',
title: 'Fairy Tale',
img: img3,
id: 3,
},
]
```
#### Challenges
- setup numbers
- don't worry about css
- hint - index (second parameter in map)
index.js
```js
const BookList = () => {
return (
{books.map((book, index) => {
return
})}
)
}
const Book = (props) => {
const { img, title, author, number } = props
return (
{title}
{author}
{`# ${number + 1}`}
)
}
```
index.css
```css
.book {
background: #fff;
border-radius: 1rem;
padding: 2rem;
text-align: center;
/* set relative */
position: relative;
}
.number {
position: absolute;
top: 0;
left: 0;
font-size: 1rem;
padding: 0.75rem;
border-top-left-radius: 1rem;
border-bottom-right-radius: 1rem;
background: #c35600;
color: #fff;
}
```
#### Add Title
- add a title to our app (css optional)
- change page title
index.js
```js
function BookList() {
return (
<>
amazon best sellers
{books.map((book) => {
return
})}
>
)
}
```
index.css
```css
h1 {
text-align: center;
margin-top: 4rem;
text-transform: capitalize;
}
```
public/index.html
```html
Best Sellers
```
#### Build Production Application
- stop the dev server "ctrl + c"
- run "npm run build"
- build folder gets created
#### Netlify
- sign up
- add new site/deploy manually
- choose build folder
- rename site - site settings/change site name