Notes for Learning React #2
🚨 It is written only for myself (It may have no reference value)
🔗 Source code in my GitHub
Rendering Lists
Simple List
- Put
booksobject in an array
const books = [
{
id: 0,
img: 'https://images-na.ssl-images-amazon.com/images/I/81bGKUa1e0L._AC_UL900_SR300,450_.jpg',
title: 'Atomic Habits',
author: 'James Clear',
},
{
id: 1,
img: 'https://images-na.ssl-images-amazon.com/images/I/71IJiOOyb1L._AC_UL900_SR300,450_.jpg',
title: 'Outlive',
author: 'Peter Attia MD',
},
];
- Map
booksmembers into a new array o JSX nodes, and return it asnewNames. (Remember: Array.prototype.map() is going to return a new array)
const names = ['John', 'Lucas', 'Harry'];
const newNames = names.map(name => <h1>{name}</h1>);
Here is the results:
import React from 'react';
import * as ReactDomClient from 'react-dom/client';
import './index.css';
const books = [
{
id: 0,
img: 'https://images-na.ssl-images-amazon.com/images/I/81bGKUa1e0L._AC_UL900_SR300,450_.jpg',
title: 'Atomic Habits',
author: 'James Clear',
},
{
id: 1,
img: 'https://images-na.ssl-images-amazon.com/images/I/71IJiOOyb1L._AC_UL900_SR300,450_.jpg',
title: 'Outlive',
author: 'Peter Attia MD',
},
];
const names = ['John', 'Lucas', 'Harry'];
const newNames = names.map(name => <h1>{name}</h1>);
function BookList() {
return <section className="booklist">{newNames}</section>;
}
const Book = props => {
const { img, title, author } = props;
return (
<article className="book">
<img className="image" src={img} alt={title} />
<h1>{title}</h1>
<h4>{author}</h4>
</article>
);
};
const root = ReactDomClient.createRoot(document.getElementById('root'));
root.render(<BookList />);
Proper List
Nothing new. It is just finished our demo by using map(). (Yeah, that's what I think).
import React from 'react';
import * as ReactDomClient from 'react-dom/client';
import './index.css';
const books = [
{
id: 1,
img: 'https://images-na.ssl-images-amazon.com/images/I/81bGKUa1e0L._AC_UL900_SR300,450_.jpg',
title: 'Atomic Habits',
author: 'James Clear',
},f
{
id: 2,
img: 'https://images-na.ssl-images-amazon.com/images/I/71IJiOOyb1L._AC_UL900_SR300,450_.jpg',
title: 'Outlive',
author: 'Peter Attia MD',
},
{
id: 3,
img: 'https://images-na.ssl-images-amazon.com/images/I/71aG+xDKSYL._AC_UL900_SR300,450_.jpg',
title: 'The 48 Laws of Power',
author: 'Robert Greene',
},
];
function BookList() {
return (
<section className="booklist">
{/* It forwarding a `book` object to parent component(Book) */}
{books.map(book => (
<Book book={book}></Book>
))}
</section>
);
}
// Every time `Book` component accepts a `book` object from `BookList` component
// So we can see 3 results are returned in the log of browser
const Book = props => {
console.log(props);
// `book` is an object of `props`
// We are not destructing the `props`
// Actually, we are destructing the 'book'
const { img, title, author } = props.book;
return (
<article className="book">
<img className="image" src={img} alt={title} />
<h1>{title}</h1>
<h4>{author}</h4>
</article>
);
};
const root = ReactDomClient.createRoot(document.getElementById('root'));
root.render(<BookList />);
Key Prop and Spread Operator
Key Prop
⚠️ JSX elements directly inside a map() call always need keys!
We should always include the key in our data
function BookList() {
return (
<section className="booklist">
{/* It forwarding a `book` object to parent component(Book) */}
{books.map(book => (
<Book key={book.id} book={book}></Book>
))}
</section>
);
}
And we can also add index as parameter in map()
function BookList() {
return (
<section className="booklist">
{/* It forwarding a `book` object to parent component(Book) */}
{books.map((book, index) => (
<Book key={index} book={book}></Book>
))}
</section>
);
}
Spread Operator
Sometimes, passing props gets very repetitive:
function BookList() {
return (
<section className="booklist">
{/* It forwarding a `book` object to parent component(Book) */}
{books.map((book, index) => (
<Book key={index} img={img} title={title} author={author}></Book>
))}
</section>
);
}
We can also use ES6 feature Spread Operator (which is more concise):
function BookList() {
return (
<section className="booklist">
{/* It forwarding a `book` object to parent component(Book) */}
{books.map((book, index) => (
<Book key={index} {...book}></Book>
))}
</section>
);
}
❓ OK, so here is the question: What's the difference between the parameter
{...book}andbook={book}in<Book />
- I think
{book}is an object that is assigned to thebookvariable(object) inbook={book}expression.- So the
bookhere is an object that contains data in another object.- The
{...book}will forward the entry instead.- So it forwards the data object itself, not the object of object.
So we will refactor the code like this:
function BookList() {
return (
<section className="booklist">
{books.map(book => (
<Book key={book.id} {...book}></Book>
))}
</section>
);
}
const Book = ({ img, title, author }) => {
return (
<article className="book">
<img className="image" src={img} alt={title} />
<h1>{title}</h1>
<h4>{author}</h4>
</article>
);
};
Event Basics
This is the results:
const Book = ({ img, title, author }) => {
const handleClick = () => {
alert('Hello World!');
};
return (
<article className="book">
<img className="image" src={img} alt={title} />
<h1>{title}</h1>
<h4>{author}</h4>
<button type="button" onClick={handleClick}>
reference example
</button>
</article>
);
};
📝 Note
We defined
handleClickfunction and passed it as a prop to<button>.Event handler function:
- Are usually defined inside our components(
Bookhere).- Have names that start with handle, followed by the name of the event.
By convention, it is common to name event handlers as
handlefollowed by the event name. We’ll often seeonClick={handleClick},onMouseEnter={handleMouseEnter}, and so on.
We have reference event and we will pass a parameter into an event handler:
const Book = ({ img, title, author }) => {
const clickHandler = () => {
alert('Hello World!');
};
const complexExample = author => {
console.log(author);
};
return (
<article className="book">
<img className="image" src={img} alt={title} />
{/* We can also make event handler as inline way */}
<h1 onClick={() => console.log(title)}>{title}</h1>
<h4>{author}</h4>
{/* We have reference `clickHandler` here ⬇️ */}
<button type="button" onClick={clickHandler}>
reference example
</button>
{/* It log in the console when rendering the page without any click event. */}
{/* Why was that happening? */}
<button type="button" onClick={complexExample(author)}>
complex example
</button>
</article>
);
};
⚠️ Pitfall:
🔴 REMEMBER: Functions passed to event handlers must be passed, not called.
onclick={handleClick}(correct) ✅onclick={handleClick(param)}(false) ❌In the first example, the
handleClickfunction is passed as an onClick event handler. This tells React to remember it and only call your function when the user clicks the button.In the second example, the
()at the end ofhandleClick()fires the function immediately during rendering, without any clicks. This is because JavaScript inside the JSX { and } executes right away.If we want to pass a parameter, we can use inline code:
onclick={() => handleClick(param)}(correct: returnhandleClick()) ✅onclick={handleClick(param)}(false: firehandleClick()) ❌I think
() => handleClick(param)inonClickis a bit hard to understand