React fundamentals and pitfalls
React fundamentals and pitfalls. Mostly from the official doc.
- React components are regular JavaScript functions whose names must always start with a capital letter
function MyButton() {
return (
<button>I'm a button</button>
);
}
- The markups must be wrapped in a pair of parentheses if they aren't all on the same line as the return keyword
return (
<div>
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
</div>
);
- JSX is stricter than HTML and tags must always be closed
<img className="avatar" />
<br />
<MyButton />
<h1>This is heading</h1>
<button>I'm a button</button>
//...
- React component can’t return multiple JSX tags which must be wrapped in a shared parent, like a
<div>...</div>
or an empty<>...</>
wrapper
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
- Components can render other components but never nest their definitions
export default function Gallery() {
// 🔴 Never define a component inside another component!
function Profile() {
// ...
}
// ...
}
- CSS class is specified with
className
and inlinestyle
properties must be written incamelCase
<img className="avatar" />
<ul style="background-color: black"> // html
<ul style={{ backgroundColor: 'black' }}> // react
- Curly braces are used to
escape back
into JavaScript for using JavaScript variables or expressions
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
- Curly braces must follow
=
immediately without"
when used for attributes and not used as dynamic variable template
// src={avatar} OK
// src="{avatar}" NG
// <h1>{name}'s To Do List</h1> OK
// <{tag}>Gregorio Y. Zara's To Do List</{tag}> NG
- Double curly braces are needed to pass objects
export default function TodoList() {
return (
<ul style={{
backgroundColor: 'black',
color: 'pink'
}}>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
);
}
- Props can be forwarded to children with
spread
syntax
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
- Props are immutable and children components have to
ask
parent component to pass new values viaset state
function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
- Parent component has a
children
prop set to the content nested inside
<Card>
<Avatar />
</Card>
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
- Conditional rendering works in different ways
// with if/else condition
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
// with ternary operator
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
// with shortcut operation
<div>
{isLoggedIn && <AdminPanel />}
</div>
- Rendering lists with
for loop
ormap
method and each item must have a unique immutable key
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
- Hooks must be called at the top level of components or other hooks
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
- Use updater function to update some state multiple times in one event
function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
- Use the spread syntax to create copies of objects or arrays for setting state instead of mutating in place
setPerson({
...person, // Copy other fields
artwork: { // but replace the artwork
...person.artwork, // with the same one
city: 'New Delhi' // but in New Delhi!
}
});
setArtists([
{ id: nextId++, name: name },
...artists // Put old items at the end
]);