「译」2022 年 React 入门

955 阅读6分钟

本文翻译自 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]

按钮第一次渲染时,count0,因为我们传递给 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 开头的函数称作 HooksuseState 是 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 开发时怎样的。