React 基礎知識與陷阱
React 基礎知識與陷阱。大部分來自官方文件。
- React 元件是常規 JavaScript 函數,其名稱必須始終以大寫字母開頭
function MyButton() {
return (
<button>I'm a button</button>
);
}
- 如果標記不全都與 return 關鍵字位於同一行,則必須用一對括號括起來
return (
<div>
<img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
</div>
);
- JSX 比 HTML 更嚴格,標籤必須始終關閉
<img className="avatar" />
<br />
<MyButton />
<h1>This is heading</h1>
<button>I'm a button</button>
//...
- React 元件無法傳回多個 JSX 標籤,這些標籤必須包裝在共用父級中,例如
<div>...</div>
或空的<>...</>
標籤
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
- 元件可以渲染其他元件,但不能嵌套它們的定義
export default function Gallery() {
// 🔴 Never define a component inside another component!
function Profile() {
// ...
}
// ...
}
- CSS 類以
className
指定,內嵌style
屬性必須以camelCase
格式書寫
<img className="avatar" />
<ul style="background-color: black"> // html
<ul style={{ backgroundColor: 'black' }}> // react
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>
);
}
- 當用於屬性時,花括號必須緊跟著
=
,且不帶"
,花括號也不能用作變數表示動態標籤
// src={avatar} OK
// src="{avatar}" NG
// <h1>{name}'s To Do List</h1> OK
// <{tag}>Gregorio Y. Zara's To Do List</{tag}> NG
- 傳遞物件變數時需要雙重花括號
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>
);
}
- 可以使用
spread
語法將 props 轉送給子元素
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
- Props 是不可變的,子元件必須透過
set state
來請求父元件傳遞新值
function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
- 父元件有一個
children
prop,代表嵌套在其中的內容
<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}
/>
);
}
- 可以以不同的方式實現條件渲染
// 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>
- 使用
for loop
或map
方法渲染列表,列表每個項目必須具有唯一的不可變鍵
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>
);
- 鉤子必須在元件或其他鉤子的頂層調用
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
- 使用更新函數在一個事件中多次更新某些狀態
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>
</>
)
}
- 使用
spread
語法建立物件或陣列的副本以設定狀態,而不是更改現有狀態
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
]);