Skip to content
This page has been auto-translated and may contain errors.View in English

组件

假设一个页面需要在三个地方显示相同的问候卡片,每个卡片有相同的标题和布局。如果你复制三次标记,那么每当设计改变时,你就有三个地方需要更新。React 的解决方案是:把这段 UI 写一次,然后以**组件**的形式复用它,一个返回 JSX(UI 描述)的 JavaScript 函数。你可以像使用 HTML 标签一样使用它,React 会调用这个函数来确定在屏幕上显示什么。

最简单的组件长这样:

jsx
function Greeting() {
  return <h1>Hello</h1>
}

Greeting 是一个返回 <h1>Hello</h1> 的函数。要让它真正显示出来,你需要在另一个组件里渲染它,像使用 HTML 标签一样使用它:

jsx
function App() {
  return (
    <div>
      <Greeting />
      <Greeting />
    </div>
  )
}

App 也是一个组件。它在一个 div 里渲染了两个 Greeting 组件,每个都会打印自己的 <h1>Hello</h1>。这就是组合:你构建小组件,然后把它们组合成更大的组件,一直到完整的应用。

这里有几条规则需要遵循。组件名要以大写字母开头。GreetingApp 之所以能用作标签,是因为 React 能把它们和普通 HTML 元素(如 divh1)区分开,普通 HTML 元素都是小写开头。返回 JSX 的组件也必须返回一个单一的根元素,就像 App 把两个 Greeting 标签用一个 div 包裹起来一样。这是初学者最常出错的地方:如果直接返回两个并行的元素而不用元素包裹,React 会报错。如果你需要返回并行元素而不想额外添加一个 div,可以用 Fragment:

jsx
function Greeting() {
  return (
    <>
      <h1>Hello</h1>
      <p>Welcome back</p>
    </>
  )
}

<></> 就是 Fragment。它把 h1p 组合成一个返回值,但不会在页面上添加额外的元素。

版本说明

老版本的 React 代码常常用类而不是函数来定义组件:

jsx
class Greeting extends React.Component {
  render() {
    return <h1>Hello</h1>
  }
}

你在现有代码库里还是会看到这样的类组件。本手册始终使用函数组件,这是当前编写 React 的标准方式。关于 React 如何从一种风格发展到另一种风格,详见 历史和版本

组件在渲染时被调用,所以要把函数体当成纯粹的计算场所,不要在其中产生副作用。给定相同的 props,组件每次都应该返回相同的 JSX。不要在函数体里直接获取数据、设置定时器或修改函数外的变量。这些操作应该放在像 useEffect 这样的 Hook 里(稍后会在 副作用 中介绍),它会在渲染完成后运行代码,而不是在渲染时。读取 DOM、写入在组件外声明的变量,或依赖于在调用间变化的值,都会破坏这个约束,导致难以追踪的 bug,因为 React 可能会在你无法控制的原因下多次调用你的组件。

JunoA component is a function 核心思想可以用一句话总结:组件就是一个函数,用 <Greeting /> 这样的标签调用它会执行该函数并显示它返回的结果。一旦理解了这一点,学习 React 的其余内容就是掌握你可以在这些函数内写什么。
JunoA component is a function 组件是你在 React 中用来构建应用的基本单位。保持组件小巧,用大写字母命名它们,并返回一个单一的根元素,当你需要组合并行元素而不想添加额外的包裹元素时,使用 Fragment。

组合,即把小组件组合成更大的组件,是构建每个真实界面的方式。

JunoA component is a function 组件是其 props 的纯函数:相同的输入产生相同的 JSX,函数体内没有副作用。这个约束正是 React 能够自由调用你的组件、批量处理渲染并重新排序工作而不会破坏任何东西的原因。

副作用有专门的 Hook 是有原因的。

接下来:JSX,你在这些 return 语句内看到的语法。