-
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
- index.html
-
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 src folder
-
create src folder
- create index.js inside src
-
toggle sidebar CMD + B
-
shortcuts settings/keyboard shortcuts
function Greeting() { return <h2>My First Component</h2> } // arrow function also works const Greeting = () => { return <h2>My First Component</h2> }- starts with capital letter
- must return JSX (html)
- always close tag
// imports or logic const Greeting = () => { return <h2>My First Component</h2> } export default Greetingindex.js
import React from 'react' import ReactDOM from 'react-dom/client' function Greeting() { return <h2>My First Component</h2> } const root = ReactDOM.createRoot(document.getElementById('root')) root.render(<Greeting />)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)
- Auto Rename Tag
- Highlight Matching Tag
- customize in settings.json
- Prettier
- format on save
- format on paste
- Default Formatter (Prettier - Code formatter)
settings.json
"editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "prettier.singleQuote": true, "prettier.semi": false,- Emmet
settings.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
- capital letter
- must return something
- JSX syntax (return html)
- to make our lives easier
- calling function under the hood
index.js
const Greeting = () => { return React.createElement('h2', {}, 'hello world') }function Greeting() { return ( <div> <h2>hello world</h2> </div> ) } const Greeting = () => { return React.createElement( 'div', {}, React.createElement('h2', {}, 'hello world') ) }-
return single element (one parent element)
- semantics section/article
- Fragment - let's us group elements without adding extra nodes
return <React.Fragment>...rest of the return</React.Fragment> // shorthand return <>...rest of the return</>- camelCase property naming convention
return ( <div tabIndex={1}> <button onClick={myFunction}>click me</button> <label htmlFor='name'>Name</label> <input readOnly={true} id='name' /> </div> ) // in html <div tabindex="1"> <button onclick="myFunction()">click me</button> <label for='name'>Name</label> <input readonly id='name' /> </div>- className instead of class
return <div className="someValue">hello</div>- close every element
return <img /> // or return <input />- formatting
- opening tag in the same line as return or ()
function Greeting() { return ( <> <div className="someValue"> <h3>hello people</h3> <ul> <li> <a href="#">hello world</a> </li> </ul> </div> <h2>hello world</h2> <input type="text" name="" id="" /> </> ) }function Greeting() { return ( <div> <Person /> <Message /> </div> ) } const Person = () => <h2>john doe</h2> const Message = () => { return <p>this is my message</p> }- top right corner
- more tools/extensions
- open chrome web store
- setup structure
import React from 'react' import ReactDOM from 'react-dom/client' function BookList() { return ( <section> <Book /> <Book /> <Book /> <Book /> </section> ) } const Book = () => { return ( <article> <Image /> <Title /> <Author /> </article> ) } const Image = () => <h2>image placeholder</h2> const Title = () => { return <h2>Book Title</h2> } const Author = () => <h4>Author</h4> const root = ReactDOM.createRoot(document.getElementById('root')) root.render(<BookList />)- in search engine type - 'amazon best selling books' Amazon Best Sellers
- DON'T NEED TO BUY ANYTHING !!!
- NOT AN AFFILIATE LINK !!!!
- choose a book
- copy image, title and author
import React from 'react' import ReactDOM from 'react-dom/client' function BookList() { return ( <section> <Book /> <Book /> <Book /> <Book /> </section> ) } const Book = () => { return ( <article className="book"> <Image /> <Title /> <Author /> </article> ) } const Image = () => ( <img src="https://images-na.ssl-images-amazon.com/images/I/71m+Qtq+HrL._AC_UL900_SR900,600_.jpg" alt="Interesting Facts For Curious Minds" /> ) const Title = () => { return <h2>Interesting Facts For Curious Minds</h2> } const Author = () => <h4>Jordan Moore </h4> const root = ReactDOM.createRoot(document.getElementById('root')) root.render(<BookList />)- create index.css in src
* { 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
import './index.css' function BookList() { return ( <section className="booklist"> <Book /> <Book /> <Book /> <Book /> </section> ) } const Book = () => { return ( <article className="book"> <Image /> <Title /> <Author /> </article> ) }- complete 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; }-
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
const Image = () => ( <img src="./images/book-1.jpg" alt="Interesting Facts For Curious Minds" /> )- whatever assets we place in public - instantly available
- domain(localhost)/asset
- style prop
- {} in JSX means going back to JS Land
- value is an object with key/value pairs - capitalized and with ''
const Author = () => ( <h4 style={{ color: '#617d98', fontSize: '0.75rem', marginTop: '0.5rem' }}> Jordan Moore </h4> )- css rules still apply (inline vs external 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
const Author = () => { const inlineHeadingStyles = { color: '#617d98', fontSize: '0.75rem', marginTop: '0.5rem', } return <h4 style={inlineHeadingStyles}>Jordan Moore </h4> }- FOR THE MOST PART, MULTIPLE APPROACHES AVAILABLE !!!
- AS LONG AS THE RESULT IS THE SAME, REALLY COMES DOWN TO PREFERENCE !!!!
- refactor to single book component (personal preference)
- remove inline css
const Book = () => { return ( <article className="book"> <img src="./images/book-1.jpg" alt="Interesting Facts For Curious Minds" /> <h2>Interesting Facts For Curious Minds</h2> <h4>Jordan Moore </h4> </article> ) }.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
const author = 'Jordan Moore' const Book = () => { const title = 'Interesting Facts For Curious Mindssssss' return ( <article className="book"> <img src="./images/book-1.jpg" alt="Interesting Facts For Curious Minds" /> <h2>{title}</h2> <h4>{author.toUpperCase()} </h4> {/* <p>{let x = 6}</p> */} <p>{6 + 6}</p> </article> ) }- toggle line comment Edit/Toggle Line Comment
- refactor/clean up
const author = 'Jordan Moore' const title = 'Interesting Facts For Curious Minds' const img = './images/book-1.jpg' function BookList() { return ( <section className="booklist"> <Book /> <Book /> </section> ) } const Book = () => { return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }// parameters const someFunc = (param1, param2) => { console.log(param1, param2) } // arguments someFunc('job', 'developer')const Book = (props) => { console.log(props) return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> {console.log(props)} </article> ) }-
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
function BookList() { return ( <section className="booklist"> <Book job="developer" /> <Book title="random title" number={22} /> </section> ) } const Book = (props) => { console.log(props) return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> <p>{props.job}</p> <p>{props.title}</p> <p>{props.number}</p> </article> ) }function BookList() { return ( <section className="booklist"> <Book author={author} title={title} img={img} /> <Book title={title} img={img} /> </section> ) } const Book = (props) => { console.log(props) return ( <article className="book"> <img src={props.img} alt={props.title} /> <h2>{props.title}</h2> <h4>{props.author} </h4> </article> ) }- setup an object
- refactor vars to properties
- copy/paste and rename
- get values for second book
- setup props
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 ( <section className="booklist"> <Book author={firstBook.author} title={firstBook.title} img={firstBook.img} /> <Book author={secondBook.author} title={secondBook.title} img={secondBook.img} /> </section> ) } const Book = (props) => { console.log(props) return ( <article className="book"> <img src={props.img} alt={props.title} /> <h2>{props.title}</h2> <h4>{props.author} </h4> </article> ) }-
there is no right or wrong - again preference !!!
-
Destructuring (object) JS Nuggets - Destructuring (object)
-
destructuring in Vanilla JS
-
saves time/typing
-
pull out the properties
-
don't need to reference object anymore
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
const Book = (props) => { const { img, title, author } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- destructure in function parameters (in our case props)
- if you have console.log(props) - it won't be defined
const Book = ({ img, title, author }) => { return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- 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
function BookList() { return ( <section className="booklist"> <Book author={firstBook.author} title={firstBook.title} img={firstBook.img} > <p> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Itaque repudiandae inventore eos qui animi sed iusto alias eius ea sapiente. </p> <button>click me</button> </Book> <Book author={secondBook.author} title={secondBook.title} img={secondBook.img} /> </section> ) } const Book = ({ img, title, author, children }) => { // rest of the logic } const Book = (props) => { const { img, title, author, children } = props console.log(props) return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> {children} </article> ) }- optional
@media screen and (min-width: 768px) { .booklist { grid-template-columns: repeat(3, 1fr); align-items: start; } } .book p { margin: 1rem 0 0.5rem; }-
refactor
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 <section className="booklist"></section> } const Book = (props) => { const { img, title, author } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- can't render objects in React
function BookList() { return <section className="booklist">{books}</section> }- map - creates a new array from calling a function for every array element.
const names = ['john', 'peter', 'susan'] const newNames = names.map((name) => { console.log(name) return <h1>{name}</h1> }) function BookList() { return <section className="booklist">{newNames}</section> }- remove names and newNames
function BookList() { return ( <section className="booklist"> {books.map((book) => { console.log(book) // return 'hello'; return ( <div> <h2>{book.title}</h2> </div> ) })} </section> ) }- render component
- pass properties one by one
function BookList() { return ( <section className="booklist"> {books.map((book) => { console.log(book) const { img, title, author } = book return <Book img={img} title={title} author={author} /> })} </section> ) }- typically it's going to be id
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 ( <section className="booklist"> {books.map((book) => { console.log(book) const { img, title, author, id } = book return <Book book={book} key={id} /> })} </section> ) }- you will see index,but it's not advised if the list is changing
function BookList() { return ( <section className="booklist"> {books.map((book, index) => { console.log(book) const { img, title, author, id } = book return <Book book={book} key={index} /> })} </section> ) }- render component
- pass entire object
- Destructuring (object) JS Nuggets - Destructuring (object)
function BookList() { return ( <section className="booklist"> {books.map((book) => { console.log(book) const { img, title, author } = book return <Book book={book} /> })} </section> ) } const Book = (props) => { const { img, title, author } = props.book return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- alternative
const Book = ({ book: { img, title, author } }) => { return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- utilize spread operator (...) - copy values
- Spread Operator
- JS Nuggets - Spread Operator
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)function BookList() { return ( <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} /> })} </section> ) } const Book = (props) => { const { img, title, author } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) } const Book = ({ img, title, author }) => { // rest of the code }- Vanilla 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
const EventExamples = () => { const handleButtonClick = () => { alert('handle button click') } return ( <section> <button onClick={handleButtonClick}>click me</button> </section> ) }- React Events
- no need to memorize them(idea is the same)
- most common
- onClick (click events)
- onSubmit (submit form )
- onChange (input change )
function BookList() { return ( <section className="booklist"> <EventExamples /> {books.map((book) => { return <Book {...book} key={book.id} /> })} </section> ) } const EventExamples = () => { const handleFormInput = () => { console.log('handle form input') } const handleButtonClick = () => { alert('handle button click') } return ( <section> <form> <h2>Typical Form</h2> <input type="text" name="example" onChange={handleFormInput} style={{ margin: '1rem 0' }} /> </form> <button onClick={handleButtonClick}>click me</button> </section> ) }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 ( <section> {/* add onSubmit Event Handler */} <form onSubmit={handleFormSubmission}> <h2>Typical Form</h2> <input type="text" name="example" onChange={handleFormInput} style={{ margin: '1rem 0' }} /> {/* add button with type='submit' */} <button type="submit">submit form</button> </form> <button onClick={handleButtonClick}>click me</button> </section> ) }- alternative approach
<button type="submit" onClick={handleFormSubmission}> submit form </button>- alternative approach
- pass anonymous function (in this case arrow function)
- one liner - less code
const EventExamples = () => { return ( <section> <button onClick={() => console.log('hello there')}>click me</button> </section> ) }- also can access event object
const EventExamples = () => { return ( <section> <form> <h2>Typical Form</h2> <input type="text" name="example" onChange={(e) => console.log(e.target.value)} style={{ margin: '1rem 0' }} /> </form> <button onClick={() => console.log('you clicked me')}>click me</button> </section> ) }- remove EventsExamples
- components are independent by default
function BookList() { return ( <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} /> })} </section> ) } const Book = (props) => { const { img, title, author } = props const displayTitle = () => { console.log(title) } return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <button onClick={displayTitle}>display title</button> <h4>{author} </h4> </article> ) }- remove button
- react data flow - can only pass props down
- alternatives Context API, redux, other state libraries
function BookList() { const someValue = 'shakeAndBake' const displayValue = () => { console.log(someValue) } return ( <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} displayValue={displayValue} /> })} </section> ) } const Book = (props) => { const { img, title, author, displayValue } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <button onClick={displayValue}>click me</button> <h4>{author} </h4> </article> ) }- initial setup
- create getBook function in booklist
- accepts id as an argument and finds the book
- Javascript Nuggets - Filter and Find
- 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
const BookList = () => { const getBook = (id) => { const book = books.find((book) => book.id === id) console.log(book) } return ( <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} getBook={getBook} /> })} </section> ) } const Book = (props) => { const { img, title, author, getBook, id } = props // console.log(props); return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> {/* this is not going to work */} <button onClick={getBook(id)}>display title</button> <h4>{author}</h4> </article> ) }- two fixes
- first option - setup wrapper
const Book = (props) => { const { img, title, author, getBook, id } = props // console.log(props); const getSingleBook = () => { getBook(id) } return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <button onClick={getSingleBook}>display title</button> <h4>{author}</h4> </article> ) }- two fixes
- second option - wrap in the anonymous arrow function
const Book = (props) => { const { img, title, author, getBook, id } = props // console.log(props); const getSingleBook = () => { getBook(id) } return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <button onClick={() => getBook(id)}>display title</button> <h4>{author}</h4> </article> ) }- remove all getBook code
function BookList() { return ( <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} /> })} </section> ) } const Book = (props) => { const { img, title, author } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) }- setup two files in src books.js and Book.js
- cut books array from index.js
- add to books.js
books.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
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
import { books } from './books'- default export
const Book = (props) => { const { img, title, author } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author} </h4> </article> ) } export default Bookindex.js
import Book from './Book'- 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
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, }, ]- setup numbers
- don't worry about css
- hint - index (second parameter in map)
index.js
const BookList = () => { return ( <section className="booklist"> {books.map((book, index) => { return <Book {...book} key={book.id} number={index} /> })} </section> ) } const Book = (props) => { const { img, title, author, number } = props return ( <article className="book"> <img src={img} alt={title} /> <h2>{title}</h2> <h4>{author}</h4> <span className="number">{`# ${number + 1}`}</span> </article> ) }index.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 a title to our app (css optional)
- change page title
index.js
function BookList() { return ( <> <h1>amazon best sellers</h1> <section className="booklist"> {books.map((book) => { return <Book {...book} key={book.id} /> })} </section> </> ) }index.css
h1 { text-align: center; margin-top: 4rem; text-transform: capitalize; }public/index.html
<title>Best Sellers</title>- stop the dev server "ctrl + c"
- run "npm run build"
- build folder gets created
- sign up
- add new site/deploy manually
- choose build folder
- rename site - site settings/change site name