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
- 花括号用于转义回 JavaScript 以使用 JavaScript 变量或表达式
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
]);