本文翻译自 Quick Start - React Beta 官方文档。新版文档以 Hooks 写法为主。
组件的创建和嵌套
React 应用由组件构成。组件就是一段代码,包含自己状态和逻辑。在 React 中,组件就是一个普通 JavaScript 函数,返回值是一些标签元素:
function MyButton() {
return (
<button>Click me</button>
);
}
创建组件 MyButton 后,可以在其他组件嵌套使用:
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
注意,<MyButton /> 以大写字母开头,这是 React 组件的独特标识。React 组件标签首字母必须是大写字母,而 HTML 原生标签必须是小写字母。
使用 JSX 编写标记
上面的标记语法叫做 JSX。使用 JSX 可以简化代码编写。所有的 React 脚手架工具开箱支持 JSX。
JSX 比 HTML 稍严。需要自关闭标签,比如 <br />。组件不能返回多个 JSX 标签。需要使用一个共同的父组件把它们包裹返回,比如 <div>...</div> 或空的 <>...</> 等:
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there. <br /> How do you do?</p>
</>
);
}
如果你有很多 HTML 标签要转换成 JSX,可以使用在线工具。
增加样式
在 React 中,使用 className 增加 CSS 类。它和 HTML 的 class 属性作用一样:
<img className="avatar" />
然后,就可以在独立的 CSS 文件中为它编写规则:
/* 在 CSS 文件中 */
.avatar {
border-radius: 50%;
}
React 没有规定如何引入 CSS 文件。最简单的做法,就是在 HTML 中增加 <link> 标签。如果你使用构建工具或框架,可以参照各自的说明文档,决定具体到的 CSS 文件引入方式。
展示数据
JSX 让你在 JavaScript 中使用标签。而花括号让你“重返” JavaScript 模式,此时你可以在代码中嵌套展示一些 JS 变量。比如,下面代码展示了 user.name 变量:
return (
<h1>
{user.name}
</h1>
);
也可以在 JSX 属性中使用 JavaScript 变量,但要记得使用花括号代替引号。比如,className="avatar" 将 "avatar" 字符串当作 CSS 类,而 src={user.imageUrl} 会读取 user.imageUrl 变量,然后将它作为 src 属性的值:
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
还可以在 JSX 花括号中使用复杂表达式,比如字符串拼接:
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
}}
/>
</>
);
}
在上面的例子中,style={{}} 不是特殊语法,而是普通的 {} 对象和 style={} JSX 花括号叠加使用的结果。
条件渲染
在 React 中,条件语句没有特殊语法。你只需要使用普通的 JS 代码即可。比如,可以使用 if 语句:
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
如果偏爱紧凑代码,可以使用 ?: 三目运算符。JSX 中不能使用 if 语句,但是可以使用三目运算符:
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
如果不需要 else 分支,还可以使用更短的逻辑与 && 语法:
<div>
{isLoggedIn && <AdminPanel />}
</div>
渲染列表
可以使用 JS 的 for 循环和数组的 map() 函数渲染组件列表。
比如,我们有如下产品列表:
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
在组件中,就可以使用 map() 函数,将产品数组转化为 <li> 元素数组:
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
注意,<li> 有一个 key 属性。列表中的每一个元素,都应该有一个独特的标识符。通常,key 来自你的数据,比如数据库 ID。React 根据 key 值辨别元素的不同操作,比如增加、删除、重新排序等。
处理事件
可以在组件内声明事件处理函数:
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
注意,onClick={handleClick} 结尾处没有圆括号!不要执行事件处理函数:只需将其传递进来即可。当用户点击按钮,React 会执行这个事件处理函数。
更新屏幕
我们经常希望组件可以“记住”某些信息,并展示出来。比如,我们想统计一个按钮的点击次数。此时,我们可以为组件增加“状态”。
首先,从 React 引入 useState:
import { useState } from 'react';
此时,我们可以在组件中声明一个状态变量:
function MyButton() {
const [count, setCount] = useState(0);
}
从 useState 中可以得到两个东西:当前状态(state),和更新状态的函数(setCount)。我们可以使用任意名称,但是惯例写法是 [something, setSomething]。
按钮第一次渲染时,count 是 0,因为我们传递给 useState() 的初始值是 0。当需要改变状态时,调用 setCount() 并将新值传给它即可。点击按钮就可以增加计数:
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
随着按钮的点击,count 会变为 1,然后变为 2,等等。
如果多次渲染同一组件,每个都会有自己的状态。尝试分别点击不同的按钮:
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
<MyButton />
</div>
);
}
使用 Hooks
以 use 开头的函数称作 Hooks。useState 是 React 提供的内置 Hook。可以从 React API reference 查看其余内置 Hooks。我们还可以利用现有 Hooks,生成新 Hooks。
Hooks 比普通函数有更多的限制。只可以在组件(或其他 Hooks)的根作用域调用 Hooks。如果需要在条件语句或循环中使用 useState,可以提取新组件,然后在新组件中使用它。
组件间分享数据
在上述例子中,每个组件都有各自独立的计数:
- MyApp
- MyButton (count: 3)
- MyButton (count: 1)
- MyButton (count: 2)
然而,有时候我们需要不同组件共享数据,同时更新状态。
如果想让所有按钮显示同样的 count,保持状态同步,需要将状态从独立的按钮提取出来,放入最近的父容器。在这个例子中,是 MyApp:
- MyApp (count: 3)
- MyButton
- MyButton
- MyButton
以下是具体的代码实现:
function MyButton({ count, onClick }) {
// 3. 使用传入的属性
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
export default function MyApp() {
// 1. 首先,把子组件的状态变量提升到父容器
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update separately</h1>
{/* 2. 将状态和事件处理函数传入每个子组件 */}
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
当你点击按钮时,会触发 onClick 处理函数。每个按钮的 onClick 属性都指向 MyApp 中的 handleClick 函数,此时会触发其中的 setCount(count + 1),增加 count 状态变量。新的 count 变量会当作属性传给每个按钮,因此它们最终都会显示相同的值。
这叫做“提升状态变量”。通过提升,我们可以在不同组件间共享状态变量。
下一步
现在,我们已经掌握了 React 的基本用法!
访问 Thinking in React,可以看看实际的 React 开发时怎样的。