1.创建一个简单的 JSX 元素

简介:React 是由 Facebook 创建和维护的开源视图库。它是渲染当代 Web 应用程序用户界面(UI)的绝佳工具。

React 使用名为 JSX 的 JavaScript 语法扩展,允许你直接在 JavaScript 中编写 HTML。这有几个好处。它允许你在 HTML 中使用 JavaScript 的完整程序功能,并有助于保持代码的可读性。在大多数情况下,JSX 类似于你已经学过的 HTML,但是在这些挑战中将会涉及一些关键差异。

例如,因为 JSX 是 JavaScript 的语法扩展,所以你实际上可以直接在 JSX 中编写 JavaScript。要做到这一点,你只需在花括号中包含你希望被视为 JavaScript 的代码:{“这被视为 JavaScript 代码”}。请牢记这个写法,你将会在接下来的挑战中使用。

但是,由于浏览器不能解析 JSX,因此必须将 JSX 代码编译为 JavaScript。在这个过程中,转换器 Babel 是一个很受欢迎的工具。

值得注意的是,这些挑战在底层调用ReactDOM.render(JSX, document.getElementById('root'))。这个函数调用是将你的 JSX 置于 React 自己的轻量级 DOM 中。然后,React 使用自己的 DOM 快照来优化更新实际 DOM 的特定部分。

2.创建一个复杂的 JSX 元素

JSX 也可以表示更复杂的 HTML。

关于嵌套的 JSX,你需要知道的一件重要的事情,那就是它必须返回单个元素。

这个父元素将包裹所有其他级别的嵌套元素。

例如,几个作为兄弟元素而编写的JSX元素没有父元素包裹将不会被转换。

这里是一个示例:

有效的 JSX:

<div>
  <p>Paragraph One</p>
  <p>Paragraph Two</p>
  <p>Paragraph Three</p>
</div>

无效的 JSX:

<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>

3.在 JSX 中添加注释

JSX 是一种可以编译成有效 JavaScript 的语法。有时,为了便于阅读,你可能需要在代码中添加注释。像大多数编程语言一样,JSX 也有自己的方法来实现这一点。

要将注释放在 JSX 中,可以使用{/* */}语法来包裹注释文本。

4.渲染 HTML 元素为 DOM 树

JSX 是一种在 JavaScript 中编写可读 HTML 的便捷工具。在 React 中,我们可以使用它的的渲染 API(ReactDOM)将此 JSX 直接渲染到 HTML DOM。

ReactDOM 提供了一个简单的方法来将 React 元素呈现给 DOM,如下所示:ReactDOM.render(componentToRender, targetNode),其中第一个参数是要渲染的 React 元素或组件,第二个参数是要将组件渲染到的 DOM 节点。

如你所料,必须在 JSX 元素声明之后调用ReactDOM.render(),就像你在使用变量之前必须声明它一样。

5.在 JSX 中定义一个 HTML Class

到目前为止,HTML 和 JSX 似乎完全相同。

JSX 的一个关键区别是你不能再使用class这个单词来定义 HTML 的 class 名。这是因为class是 JavaScript 中的关键字。JSX 使用className代替。

事实上,JSX 中所有 HTML 属性和事件引用的命名约定都变成了驼峰式。例如,JSX 中的单击事件是onClick,而不是onclick。同样,onchange变成了onChange。虽然这是一个微妙的差异,但请你一定要记住。

6.了解自动闭合的 JSX 标记

JSX 与 HTML 的不同之处在于使用className和使用class来定义 HTML 的 class。

JSX 不同于 HTML 的另一个重要方面是自闭合标签。

在HTML中,几乎所有的标签都有一个开始和结束标签:<div></div>,结束标签在你要关闭的标签名之前始终具有正斜杠。但是,HTML 中有一些称为“自闭合标签”的特殊实例,它们在另一个标签开始之前,不需要开始和结束标签都存在。

例如,换行标签可以写成<br>或者<br />,但是不应该写成<br></br>,因为它不包含任何内容。

在 JSX 中,规则略有不同。任何 JSX 元素都可以使用自闭合标签编写,并且每个元素都必须关闭。例如,换行标签必须始终编写为<br />。另一方面

可以写成<div />或者<div></div>。不同之处在于,在第一个语法版本中,无法在<div />中包含任何内容。

7.创建一个无状态的函数组件

组件是 React 的核心。React 中的所有内容都是一个组件,在这里你将学习如何创建一个组件。

有两种方法可以创建 React 组件。第一种方法是使用 JavaScript 函数。以这种方式定义组件会创建无状态功能组件。应用程序中的状态概念将在以后的挑战中介绍。目前,可以将无状态组件视为可以接收数据并对其进行渲染的组件,但是它不管理或跟踪对数据的更改,我们将在下一次挑战中介绍创建 React 组件的第二种方法。

要用函数创建组件,只需编写一个返回 JSX 或null的 JavaScript 函数。需要注意的一点是,React 要求你的函数名以大写字母开头。下面是一个无状态功能组件的示例,该组件在 JSX 中分配一个 HTML 的 class:

// After being transpiled, the <div> will have a CSS class of 'customClass'
const DemoComponent = function() {
  return (
    <div className='customClass' />
  );
};

因为 JSX 组件代表 HTML,所以你可以将几个组件放在一起以创建更复杂的 HTML 页面,这是 React 提供的组件架构的关键优势之一,它允许你用许多独立的组件组成 UI。这使得构建和维护复杂的用户界面变得更加容易。

8.创建一个 React 组件

定义 React 组件的另一种方法是使用 ES6 的class语法。在以下示例中,Kitten扩展了React.Component

class Kitten extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <h1>Hi</h1>
    );
  }
}

这将创建一个 ES6 类Kitten,它扩展了React.Component类。因此,Kitten类现在可以访问许多有用的 React 功能,例如本地状态和生命周期钩子。如果你还不熟悉这些术语,请不要担心,在以后的挑战中我们将更详细地介绍它们。

另请注意,Kitten类中定义了一个调用super()方法的constructor。它使用super()调用父类的构造函数,即本例中的React.Component。构造函数是使用class关键字创建的特殊方法,它用在实例初始化之前。最佳做法是在组件的constructor里调用super,并将props传递给它们,这样可以保证组件能够正确地初始化。现在,你只需要知道这是标准的做法。很快你会看到构造函数的其他用途以及props。

9.用组合的方式创建一个 React 组件

现在我们来看看如何组合多个 React 组件。想象一下,你正在构建一个应用程序,并创建了三个组件:NavbarDashboardFooter

要将这些组件组合在一起,你可以创建一个App父组件,将这三个组件分别渲染成为子组件。要在 React 组件中渲染一个子组件,你需要在 JSX 中包含作为自定义 HTML 标签编写的组件名称。例如,在render方法中,你可以这样编写:

return (
<App>
  <Navbar />
  <Dashboard />
  <Footer />
</App>
)

当 React 遇到引用另一个组件的自定义 HTML 标签时(如本例所示,组件名称包含在< />中),它在标签的位置渲染该组件的标签。这可以说明App组件和NavbarDashboard以及Footer之间的父子关系。

10.使用 React 渲染嵌套组件

上一个挑战显示了组合两个组件的简单方法,但是有许多不同的方法可以把 React 组件组合在一起。

组件组合是 React 的强大功能之一。当你使用 React 时,应当先用组件的思路考虑清楚用户界面的结构(如上一个挑战中的 App 示例)。可以将 UI 分解为基本的构建块,这些构建块就是组件。这样做有助于将负责 UI 的代码与负责处理应用程序逻辑的代码分开,并可以大大简化复杂项目的开发和维护。

11.组合 React 组件

随着挑战继续,我们将组合使用更复杂的 React 组件和 JSX,有一点需要注意。在其他组件中渲染 ES6 风格的类组件和渲染你在过去几个挑战中使用的简单组件没有什么不同。你可以在其他组件中渲染 JSX 元素、无状态功能组件和 ES6 类组件。

12.渲染 class 组件为 Dom 树

你可能还记得在早期挑战中使用 ReactDOM API 将 JSX 元素渲染到 DOM,这与渲染 React 组件的过程十分相似。过去的几个挑战主要针对组件和组合,因此渲染是在幕后为你完成的。但是,如果不调用 ReactDOM API,你编写的任何 React 代码都不会渲染到 DOM。

以下是语法的复习:ReactDOM.render(componentToRender, targetNode)。第一个参数是要渲染的 React 组件。第二个参数是要在其中渲染该组件的 DOM 节点。

React 组件传递到ReactDOM.render()与 JSX 元素略有不同。对于 JSX 元素,你传入的是要渲染的元素的名称。但是,对于 React 组件,你需要使用与渲染嵌套组件相同的语法,例如ReactDOM.render(<ComponentToRender />, targetNode)。你可以将此语法用于ES6类组件和函数组件。

13.从零开始写一个 React 组件

现在你已经了解了 JSX 和 React 组件的基础知识,现在是时候自己编写一个组件了。React 组件是 React 应用程序的核心组成部分,因此熟练编写它们是非常重要的。记住,典型的 React 组件是 ES6class,它扩展了React.Component。它有一个返回 HTML(从 JSX 返回)或null的渲染方法,这是 React 组件的基本形式。一旦你深刻地理解了这一点,你就可以开始构建更复杂的 React 项目了。

14.将 Props 传递给无状态函数组件

之前的挑战涵盖了关于在 React 中创建和组合 JSX 元素、函数组件和 ES6 风格的类组件的很多内容。有了这个基础,现在是时候看看 React 中的另一个常见特性 props 了。在 React 中,你可以将属性传递给子组件。假设你有一个App组件,该组件渲染了一个名为Welcome的子组件,它是一个无状态函数组件。你可以通过以下方式给Welcome传递一个user属性:

<App>
  <Welcome user='Mark' />
</App>

使用自定义 HTML 属性,React 支持将属性user传递给组件Welcome。由于Welcome是一个无状态函数组件,它可以像这样访问该值:

const Welcome = (props) => <h1>Hello, {props.user}!</h1>

调用props这个值是常见做法,当处理无状态函数组件时,你基本上可以将其视为返回 JSX 的函数的参数。这样,你就可以在函数体中访问该值。但对于类组件,访问方式会略有不同。

15.传递一个数组作为 Props

上一个挑战演示了如何将来自父组件的信息作为props传递给子组件。这个挑战着眼于如何将数组作为props传递。要将数组传递给 JSX 元素,必须将其视为 JavaScript 并用花括号括起来。

<ParentComponent>
  <ChildComponent colors={["green", "blue", "red"]} />
</ParentComponent>

这样,子组件就可以访问数组属性colors。访问属性时可以使用join()等数组方法。

const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>

这将把所有colors数组项连接成一个逗号分隔的字符串并生成:

<p>green, blue, red</p>

稍后,我们将了解在 React 中渲染数组数据的其他常用方法。

16.使用默认的 Props

React 还有一个设置默认 props 的选项。你可以将默认 props 作为组件本身的属性分配给组件,React 会在必要时分配默认 prop。如果没有显式的提供任何值,这允许你指定 prop 值应该是什么。例如,如果你声明MyComponent.defaultProps = { location: 'San Francisco' },即定义一个 location 属性,并且其值在没有另行制定的情况下被设置为字符串San Francisco。如果 props 未定义,则 React 会分配默认 props,但如果你将null作为 prop 的值,它将保持null

17.覆盖默认的 Props

在 React 中,设置默认的 props 是一个很有用的特性,显式设置组件的 prop 值即可覆盖默认 props。

18.使用 PropTypes 来定义你期望的 Props

React 提供了有用的类型检查特性,以验证组件是否接收了正确类型的 props。例如,你的应用程序调用 API 来检索你希望在数组中的数据,然后将数据作为 prop 传递给组件。你可以在组件上设置propTypes,以要求数据的类型为array。当数据是任何其他类型时,都会抛出警告。

当你提前知道 prop 的类型时,最好的做法是设置propTypes。可以为组件定义propTypes属性,方法与定义defaultProps相同。这样做将检查给定键的 prop 是否是给定类型。这里有一个示例,名为handleClick的 prop 应为function类型:

MyComponent.propTypes = { handleClick: PropTypes.func.isRequired }

在上面的示例中,PropTypes.func部分检查handleClick是否为函数。添加isRequired是为了告诉 ReacthandleClick是该组件的必需属性。如果未提供该 prop,你将看到警告信息。另请注意,func表示function。在 7 种 JavaScript 基本类型中,functionboolean(写为bool)是仅有的使用异常拼写的两种类型。除了基本类型,还有其他类型可用。例如,你可以检查 prop 是否为 React 组件,请参阅文档以获取所有选项。

注意:在 React v15.5.0 版本中, PropTypes可以从 React 中单独引入,如下所示:

import React, { PropTypes } from 'react';

19.使用 this.props 访问 Props

前几项挑战涵盖了将 props 传递给子组件的基本方法。但是,倘若接收 prop 的子组件不是无状态函数组件,而是一个 ES6 类组件又当如何呢?ES6 类组件访问 props 的方法略有不同。

任何时候,只要引用类组件本身,就要使用this关键字。要访问类组件中的 props,你需要在在访问它的代码前面添加this。例如,如果 ES6 类组件有一个名为data的 prop,你可以在 JSX 中这样写:{this.props.data}

20.复习如何使用 Props 和无状态函数组件

除了上一个挑战,你一直在将 props 传递给无状态的函数组件。这些组件就像纯函数,它们接收 props 作为输入,并在每次传递相同 props 时返回相同的视图。你可能会想知道什么是状态,下一个挑战将会更详细地讲述它。在此之前,我们先来回顾一下组件的术语。

无状态函数组件是一个函数,它接收 props 作为输入并返回 JSX。另一方面,无状态组件是一个类,它扩展了React.Component,但是不使用内部状态(下一个挑战中讨论)。最后,状态组件是指维护其自身内部状态的组件,它简称组件或 React 组件。

一种常见的应用模式是尽可能减少状态组件并创建无状态的函数组件。这有助于将状态管理包含到应用程序的特定区域。反过来,通过更容易地跟踪状态变化如何影响其行为,可以改进应用程序的开发和维护。

21.创建一个有状态的组件

React中最重要的主题之一是state。 state 包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。你希望应用程序能够响应 state 的变更,并在必要时显示更新后的 UI。React 为现代 Web 应用程序的状态管理提供了一个很好的解决方案。

你可以通过在constructor中的组件类上声明state属性来在 React 组件中创建 state,它在创建时使用state初始化组件。state属性必须设置为 JavaScript对象。声明如下:

this.state = {
  // describe your state here
}

你可以在组件的整个生命周期内访问state对象,你可以更新它、在 UI 中渲染它,也可以将其作为 props 传递给子组件。state对象的使用可以很简单,亦可以很复杂,就看你怎么用了。请注意,你必须通过扩展React.Component来创建类组件,以便像这样创建state

22.在用户界面中渲染状态

一旦定义了组件的初始 state,你就可以在要渲染的 UI 中显示它的任何部分。如果组件是有状态的,它将始终可以访问render()方法中state的数据。你就可以使用this.state访问数据。

如果你想在 render方法的return中访问 state 值,你必须把这个值用花括号括起来。

state是 React 组件中最强大的特性之一,它允许你跟踪应用程序中的重要数据,并根据数据的变化渲染 UI。如果你的数据发生变化,你的 UI 也会随之改变。React 使用所谓的虚拟 DOM 来跟踪幕后的变化。当 state 数据更新时,它会使用该数据触发组件的重新渲染–包括接收 prop 数据的子组件。React 只在必要的时候更新实际的DOM,这意味着你不必担心 DOM 的变更,只需声明 UI 的外观即可。

注意,如果组件有状态,则没有其他组件知道它的state。它的state是完全封装的,或者是局限于组件本身的,除非你将 state 数据作为props传递给子组件。封装state的概念非常重要,因为它允许你编写特定的逻辑,然后将该逻辑包含并隔离在代码中的某个位置。

23.以另一种方式在用户界面中渲染状态

还有另一种方法可以访问组件中的state。在render()方法中,在return语句之前,你可以直接编写 JavaScript。例如,你可以声明函数、从state或props访问数据、对此数据执行计算等。然后,你可以将任何数据赋值给你在return语句中可以访问的变量。

24.用 this.setState 设置状态

前面的挑战涵盖了组件的state以及如何在constructor中初始化 state。还有一种方法可以更改组件的state,React 提供了setState方法来更新组件的state。在组件类中调用setState方法如下所示:this.setState(),传入键值对的对象,其中键是 state 属性,值是更新后的 state 数据。例如,如果我们在 state 中存储username,并想要更新它,代码如下所示:

this.setState({
  username: 'Lewis'
});

React 希望你永远不要直接修改state,而是在 state 发生改变时始终使用this.setState()。此外,你应该注意,React 可以批量处理多个 state 更新以提高性能。这意味着通过setState方法进行的 state 更新可以是异步的。setState方法有一种替代语法可以解决异步问题,虽然这很少用到,但是最好还是记住它!有关详细信息,请参阅React 文档

25.将 this 绑定到 Class 方法

除了设置和更新state之外,你还可以为组件类定义方法。类方法通常需要使用this关键字,以便它可以访问方法中类的属性(例如stateprops )。有几种方法可以让你的类方法访问this

一种常见的方法是在构造函数中显式地绑定this,这样当组件初始化时,this就会绑定到类方法。你可能已经注意到上一个挑战使用了this.handleClick = this.handleClick.bind(this)用于其在构造函数中的handleClick方法。然后,当你在类方法中调用像this.setState()这样的函数时,this指的是这个类,而不是undefined。

26.使用 State 切换元素

你可以用比你目前所见的更复杂的方式在 React 应用程序中使用state。例如,监视值的状态,然后根据此值有条件地渲染 UI。有几种不同的方法可以实现这一点,代码编辑器展示了其中一种。

27.React:写一个简单的计数器

你可以结合目前为止所涵盖的概念来设计更复杂的有状态组件。这包括初始化state,编写设置state的方法,以及指定单击处理程序来触发这些方法。

Counter组件跟踪state中的count值。有两个按钮分别调用increment()decrement()方法。编写这些方法,使计数器值在单击相应按钮时增加或减少 1。另外,创建一个reset()方法,当单击 reset 按钮时,把计数设置为 0。

注意: 确保你没有修改按钮的classNames。另外,请记住在构造函数中为新创建的方法添加必要的绑定。


See Also