Skip to content

Latest commit

 

History

History
819 lines (578 loc) · 29.9 KB

File metadata and controls

819 lines (578 loc) · 29.9 KB

二、创建组件

在本章中,我们将学习如何实现 React 组件,如何将多个组件组装成一个组件,以及如何管理它们的内部状态。我们将通过构建一个简单的应用程序来探索 React 组件的实现。该应用程序将逐步实施,以便将概述的概念付诸实践。

在本章结束时,您将能够:

  • 创建基本组件
  • 使用 JSX 定义组件的标记
  • 组合多个 React 组件以创建复杂的 UI 元素
  • 管理组件的内部状态

组件的定义

如前一章所定义,组件是 React 的基本构建块。实际上,用户界面中的任何可视项都可以是组件。从形式上看,我们可以说 React 组件是定义用户界面一部分的 JavaScript 代码。

在文件中考虑下面的代码:

import React from 'react';

class Catalog extends React.Component {
  render() {
    return <div><h2>Catalog</h2></div>;
  }
}

export default Catalog;

这是一个 ECMAScript 2015 模块,定义了一个基本的 React 组件。

它从react模块导入React名称空间,并通过扩展React.Component类定义Catalog类。模块将Catalog类导出为默认导出。

这个定义中有趣的部分是render()方法的实现。

render()方法定义组件的可视部分。它可以执行任何 JavaScript 代码,并且应该返回一个定义其可视输出的标记表达式。React 组件必须存在render()方法。在我们的示例中,render()方法返回以下标记:

<div><h2>Catalog</h2></div>

它看起来像 HTML;虽然它使用类似的语法,但它定义了称为元素的普通对象。React 元素类似于文档对象模型DOM元素),但更轻、更高效。因此,React 组件生成一组 React 元素,这些元素将由库引擎映射到 DOM 元素。这组 React 元素称为虚拟 DOM,是浏览器 DOM 的轻量级表示。React 只在严格必要时才负责更新 DOM 以匹配虚拟 DOM。这种方法允许 React 在呈现用户界面时具有非常高的性能。

render()方法必须符合几个约束条件:

  • 它是强制性的;也就是说,每个 React 组件都必须实现它
  • 它必须返回一个 React 元素;即,包含任何嵌套元素的单个标记项
  • 它应该是一个纯函数;也就是说,它不应更改组件的内部状态(我们将在下一节中进一步详细讨论此主题)
  • 它不应该直接与浏览器交互;也就是说,它不应该包含试图访问 DOM 的语句

A pure function is a function whose output result depends only on its input data, and its execution has no side effect, like, for example, updating a global variable. Given an input value, a pure function always returns the same result.

A pure component is a component that acts like a pure function. This means that, given the same initial conditions, it always renders the same output.

It is very important to keep the render() method a pure function. This avoids weird bugs, as we will see in the next chapter.

一旦我们定义了我们的组件,我们就可以将它用作任何其他 React 组件中的 React 元素。例如,我们知道 React 应用程序本身已经是一个 React 组件。让我们回忆一下App.js文件中create-react-app工具生成的代码:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

我们可以看到,该代码与我们定义的Catalog组件具有相同的结构。为了在App组件中使用我们的组件,让我们更改此代码:

import React, { Component } from 'react';
import './App.css';
import Catalog from './Catalog';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <h1 className="App-title">The Catalog App</h1>
        </header>
        <Catalog />
      </div>
    );
  }
}

export default App;

我们通过删除一些自动生成的标记来简化代码,然后导入Catalog组件,并将<Catalog />元素放在应用程序的render()方法返回的<div>元素中。

构建我们的第一个 React 组件

以下步骤打开现有项目my-shop-01,以显示先前代码更改的结果:

  1. 打开控制台窗口
  2. 移动到my-shop-1文件夹
  3. 运行npm install
  4. 运行npm start

下面是我们将在浏览器窗口中看到的示例:

我们已经构建了我们的第一个 React 组件,我们可以看到它在运行!

管理风格

也许您已经注意到,我们在App组件模块中有一个关于 CSS 文件的import语句:

import React, { Component } from 'react';
import './App.css';
import Catalog from './Catalog';

这似乎有点奇怪,因为import语句应该只适用于 JavaScript 代码。但是,由于create-react-app提供的开发环境,我们可以使用相同的语法,即使是 CSS 文件。这允许我们在我们的组件中使用App.css中定义的类和其他 CSS 定义,保持组件特定的样式接近组件定义本身。例如,如果我们希望我们的Catalog组件的标题为红色,我们可以按照下面所示进行操作。

添加 CSS

我们现在将更改现有项目my-shop-01的内容,以便添加一些 CSS 代码并以红色显示目录标题:

  1. 打开控制台窗口。
  2. 移动到my-shop-1/src文件夹。
  3. 创建一个文件Catalog.css,并添加以下代码:
h2 { color: red }
  1. 打开Catalog.js文件,添加语句导入Catalog.css模块,如下图:
import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    return <div><h2>Catalog</h2></div>;
  }
}
export default Catalog;
  1. 运行npm start并查看结果。

You can find a project ready in the my-shop-02 folder at Code/Chapter-2

浏览器将以红色显示目录标题:

The CSS import is not a React feature, and it is not required by React. It is a convenience provided by the development environment, built by create-react-app. In particular, this feature is provided by webpack, one of the most frequently used bundlers and module loaders. You should take this aspect into account when you want to migrate the application into a development environment not based on webpack.

活动:定义购物车

场景

我们的电子商店需要一辆购物车。

瞄准

此活动的目的是开始使用 React 定义组件。

完成步骤

  1. 我们应该定义一个 React 组件,作为购物车的基础
  2. 它应该是一个只显示字符串Cart的组件

Create a new React application by using create-react-app, and change the application, as shown in the current section.

溶液

一种可能的解决方案是包含在Code/Chapter-2/my-cart-01文件夹中的解决方案。

使用 JSX

在前面的示例中,我们使用类似 HTML 的标记表达式定义了组件的render()方法返回的可视输出。例如,让我们看看Catalog组件的定义:

import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    return <div><h2>Catalog</h2></div>;
  }
}

export default Catalog;

标记表达式未使用 JavaScript 语法,但包含在 JavaScript 代码段中。为什么我们混合使用 HTML 和 JavaScript 语法?这怎么可能?

让我们首先说,描述 React 组件的可视化输出的类似 HTML 的语言称为JSX。该语言使用 XML 表达式扩展 JavaScript,以简化 JavaScript 代码中 HTML 元素的创建。你可能会认为它是一种document.write("..."),但更强大。事实上,在构建 React 应用程序时,JSX 标记由特定的解析器进行预处理,以生成纯 JavaScript 代码。因此,我们可以利用使用声明性标记语言的简单性,该语言将自动转换为优化的 JavaScript 代码。

如前所述,JSX 表达式创建 React 元素,它是 HTML 元素的对应项。从语法的角度来看,JSX 表达式是包含任何嵌套元素的单个标记项。因此,以下是一个有效的 JSX 表达式:

<div><h2>Catalog</h2></div>

以下是无效的 JSX 表达式,因为它包含两个标记项:

<div><h2>Catalog</h2></div>
<div><img src="image.png" /></div>

JSX expressions are XML fragments, so they are compliant with XML syntax rules. This means that, among other things, the markup is case-sensitive, and all of the tags must be closed.

For example, the following JSX expression is not valid: <img src="image.png"> Its valid version is the following: <img src="image.png"/>

我们可以将 JSX 表达式分配给变量,如下例所示:

import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    let output = <div><h2>Catalog</h2></div>;
    return output;
  }
}

export default Catalog;

我们还可以通过将任何 JavaScript 表达式用大括号括起来,将其嵌入 JSX 表达式中,如下例所示:

import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    let title = "Catalog";
    return <div><h2>{title}</h2></div>;
  }
}
export default Catalog;

当然,JavaScript 表达式可以是我们所需要的那样复杂,如以下组件定义中所示:

import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    let title = "The Catalog of today " + new Date().toDateString();
    return <div><h2>{title}</h2></div>;
  }
}

export default Catalog;

In addition to optimizing output rendering, JSX also provides support to prevent injection attacks. In fact, any value embedded in a JSX expression escapes before being rendered. This, for example, prevents malicious code from being inserted by a user's input.

JavaScript 和 JSX 表达式组合的一种常见用法称为条件呈现;也就是说,这是一种允许您基于某些布尔条件生成 JSX 表达式的技术。考虑下面的例子:

import React from 'react';
import './Message.css';

class Message extends React.Component {
  render() {
    let message;
    let today = new Date().getDay();

    if (today == 0) {
 message = <div className="sorry">We are closed on Sunday...</div>;
 } else {
 message = <div className="happy">How can we help you?</div>
 }

    return message;
  }
}

export default Message;

在前面的示例中,render()方法根据一周中的当前日期返回一条或另一条消息。这导致使用不同的消息和 CSS 类生成 React 元素,但我们甚至可以返回完全不同的标记。

可以将 JSX 表达式放在多行中,如下所示:

import React from 'react';
import './Catalog.css';

class Catalog extends React.Component {
  render() {
    let title = "Catalog";

    return <div>
 <h2>{title}</h2>
 </div>;
  }
}

export default Catalog;

It is very important when returning a JSX expression to start it in the same line of the return statement, as in the previous example. If you want to start the JSX expression on a new line, you need to enclose it in round brackets and put the left bracket on the same line as the return statement, as in the following example: return (   <div>     <h2>Catalog</h2>   </div>);

您可以使用用花括号括起来的 JavaScript 语法将注释放在 JSX 表达式中。以下是带注释的 JSX 表达式示例:

<div>
  <h2>Catalog</h2>
  {//This is a comment}
 {/* This is a comment, too */}
</div>;

JSX 标记与 HTML 标记匹配,这就是为什么我们可以使用整个 HTML 语法来定义 JSX 元素。但是,有一些限制:

  • 所有 HTML 标记都是小写的
  • 您需要使用className而不是class属性
  • 您需要使用htmlFor而不是for属性

下面的示例显示如何使用className属性而不是class

<div className="catalog-style">
  <h2>Catalog</h2>
</div>;

JSX uses the className and htmlFor attributes instead of class and for because as JSX expressions are inside JavaScript, class and for could clash with the corresponding reserved keywords.

活动:将 HTML 翻译成 JSX

场景

图形部门为您提供了一个 HTML 代码段,您需要将其转换为 JSX 以创建 React 组件。

瞄准

本活动的目的是了解 HTML 和 JSX 之间的区别。

完成步骤

  1. 打开Code02.txt文件
  2. 将它包含的 HTML 代码转换为 JSX

溶液

一种可能的解决方案是包含在Code/Chapter-2/activity-b.html文件中的解决方案。

组成成分

在定义 React 组件时,我们可以通过将该组件用作 React 元素,将其用作另一个组件的子组件。当我们将Catalog成分包含在App成分中时,我们已经看到了这一点,但让我们进一步分析这个成分。

组合组件

现在,我们将了解如何组合组件以创建新的复杂组件:

  1. 打开my-shop-03文件夹中的src/ProductList.js文件
  2. 跟随课文直到本节结束

让我们考虑下面的组成部分:

import React from 'react';
class ProductList extends React.Component {
  render() {
    return <ul>
      <li>
        <h3>Traditional Merlot</h3>
        <p>A bottle of middle weight wine, lower in tannins (smoother), 
           with a more red-fruited flavor profile.</p>
      </li>
      <li>
        <h3>Classic Chianti</h3>
        <p>A medium-bodied wine characterized by a marvelous freshness with 
           a lingering, fruity finish</p>
      </li>
      <li>
        <h3>Chardonnay</h3>
        <p>A dry full-bodied white wine with spicy, bourbon-y notes in an 
           elegant bottle</p>
      </li>
      <li>
        <h3>Brunello di Montalcino</h3>
        <p>A bottle red wine with exceptionally bold fruit flavors, 
           high tannin, and high acidity</p>
      </li>
    </ul>;
  }
}
export default ProductList;

此组件定义葡萄酒、名称和描述的列表。

我们想将我们的Catalog组件与葡萄酒列表整合。既然我们已经创建了ProductList组件,我们就可以在Catalog组件的 JSX 标记中将其用作标记,如下所示:

import React from 'react';
import ProductList from './ProductList';

class Catalog extends React.Component {
  render() {
    return <div>
      <h2>Catalog</h2>
      <ProductList />
    </div>;
  }
}

export default Catalog;

如您所见,我们只是简单地导入了ProductList组件,以便在Catalog组件的模块中使用它,并且我们使用了ProductList标记来显示酒单。

运行npm start启动应用程序。生成的页面将如下所示:

We said that the HTML tags in JSX expressions should always be in lowercase. However, we used the ProductList tag in Pascal case.

与组件对应的标记必须遵循类定义中使用的大小写,按照惯例,组件类名称使用 Pascal 大小写,即使 React 不需要它。

按照上一章提供的指导原则,React 组件的易组合性使得创建用户界面变得非常简单。我们可以将页面布局分解为一组分层的组件,每个组件都由其他组件组成。这种方法允许我们关注单个组件的行为,并提高其可重用性。

活动:定义组合购物车

场景

我们想为我们的购物车创建一些内容。

瞄准

本活动的目的是合成反应组分。

完成步骤

集成先前创建的Cart组件,以包含一个CartList组件,显示两项。

溶液

一种可能的解决方案是包含在Code/Chapter-2/my-cart-02文件夹中的解决方案。

数据传播

我们在上一节中定义的组件是不实用的。让我们再看一遍:

import React from 'react';
import './ProductList.css';

class ProductList extends React.Component {
  render() {
    return <ul>
      <li>
        <h3>Traditional Merlot</h3>
        <p>A bottle of middle weight wine, lower in tannins (smoother), 
           with a more red-fruited flavor profile.</p>
      </li>
      <li>
        <h3>Classic Chianti</h3>
        <p>A medium-bodied wine characterized by a marvelous freshness with 
           a lingering, fruity finish</p>
      </li>
      <li>
        <h3>Chardonnay</h3>
        <p>A dry full-bodied white wine with spicy, bourbon-y notes in an 
           elegant bottle</p>
      </li>
      <li>
        <h3>Brunello di Montalcino</h3>
        <p>A bottle of red wine with exceptionally bold fruit flavors, high 
           tannin, and high acidity</p>
      </li>
      </ul>;
   }
}

export default ProductList;

列表项都定义为 JSX 标记,因此如果需要更改目录产品的图形外观,则需要更改每个<li>元素的所有引用。

通过进一步分解用户界面,我们可以实现更好的实现。我们可以将每个列表项看作一个组件,并且将其作为下面代码定义的一个组件:

import React from 'react';

class Product extends React.Component {
  render() {
    return <li>
      <h3>Product name</h3>
      <p>Product description</p>
    </li>;
  }
}

export default Product;

此代码充当每个列表项的模板,以便我们可以动态构建产品列表,如下所示:

import React from 'react';
import './ProductList.css';
import Product from './Product';

class ProductList extends React.Component {
  render() {
    let products = [
      {code:"P01", name: "Traditional Merlot", description: "A bottle 
       of middle weight wine, lower in tannins (smoother), with a 
       more red-fruited flavor profile."},
      {code:"P02", name: "Classic Chianti", description: "A medium-bodied
       wine characterized by a marvelous freshness with a lingering, 
       fruity finish"},
      {code:"P03", name: "Chardonnay", description: "A dry full-bodied
       white wine with spicy, bourbon-y notes in an elegant bottle"},
      {code:"P04", name: "Brunello di Montalcino", description: "A bottle
       of red wine with exceptionally bold fruit flavors, high tannin,
       and high acidity"}
    ];
    let productComponents = [];

    for (let product of products) {
      productComponents.push(<Product/>);
    }

    return <ul>{productComponents}</ul>;
  }
}

export default ProductList;

我们可以看到对象数组的定义,products,其中包含每个产品的相关数据。第二个数组productComponents将包含通过将产品数据与Product组件的标记合并而创建的 React 组件列表。for循环用于执行这种合并。最后,将返回由<ul>元素包围的结果productComponents数组。

即使代码结构看起来是正确的,结果也不会如预期的那样。事实上,我们将获得一个带有固定名称和描述的项目列表,我们将其放入Product组件定义中。换句话说,数据和组件定义之间的合并没有发生。

实际上,我们需要一种方法将每个产品的数据传递给Component类。让我们把 React 组件看作是普通的 JavaScript 函数。它们可以实现为返回 React 元素的函数,并且,与任何函数一样,组件可以具有数据输入。此类数据输入通过 JSX 属性传递,并可通过名为**props**的特殊对象在组件内部访问。让我们更改ProductList组件的代码,以便通过 JSX 属性传递数据:

import React from 'react';
import Product from './Product';

class ProductList extends React.Component {
  render() {
    let products = [
      {code:"P01", name: "Traditional Merlot", description: "A bottle
       of middle weight wine, lower in tannins (smoother), with a 
       more red-fruited flavor profile."},
      {code:"P02", name: "Classic Chianti", description: "A medium-bodied
       wine characterized by a marvelous freshness with a lingering, 
       fruity finish"},
      {code:"P03", name: "Chardonnay", description: "A dry full-bodied
       white wine with spicy, bourbon-y notes in an elegant bottle"},
      {code:"P04", name: "Brunello di Montalcino", description: "A bottle
       of red wine with exceptionally bold fruit flavors, high tannin, 
       and high acidity"}
    ];

    let productComponents = [];

    for (let product of products) {
      productComponents.push(<Product
      item={product}/>);
    }

    return <ul>{productComponents}</ul>;
  }
}

export default ProductList;

我们在<Product>标记中添加了一个item属性,并将products数组中的一个对象分配给它。这允许我们将每个产品的数据传递给Product组件。

另一方面,为了接收和管理传递的数据,我们修改了Product组件的代码:

import React from 'react';

class Product extends React.Component {
  render() {
    return <li>
      <h3>{this.props.item.name}</h3>
      <p>{this.props.item.description}</p>
    </li>;
  }
}

export default Product;

You can find a project ready in the my-shop-04 folder at Code/Chapter-2/.

每个反应组分都有一个props属性。此属性的目的是收集传递给组件本身的数据输入。只要将 JSX 属性附加到 React 元素,具有相同名称的属性就会附加到props对象。因此,我们可以使用附加属性访问传递的数据。在我们的示例中,我们发现通过映射到this.props.item属性的item属性传递的产品数据。

props are immutable; that is, they are read-only properties.

这个新的实现允许像以前一样显示目录,但使图形标记独立于产品的数据。

在组件层次结构中,数据传播非常重要。它允许我们将组件视为具有输入和输出的函数。此外,props的不变性允许我们将组件视为纯函数,这些函数没有副作用(因为它们不会更改输入数据)。我们可以将从一个组件传递到另一个组件的数据视为单向数据****流,从父组件流向子组件。这给了我们一个更可控的系统。

下图显示了我们如何想象组件层次结构中的数据传播,理想情况下:

状态的更改会导致通过props属性向子组件传播数据。

活动:创建购物车项目组件

场景

我们希望使CartList组件成为一个动态组件,以便它能够根据接收到的数据调整其内容。

瞄准

此活动的目的是组成 React 组件并在它们之间传递数据。

完成步骤

  1. 创建一个显示项目名称的CartItem组件。
  2. 更改先前创建的CartList组件,使其基于items数组由CartItem实例动态组成。

溶液

一种可能的解决方案是包含在Code/Chapter-2my-cart-03文件夹中的解决方案。

管理内部状态

组件能够存储随时间变化的数据。

当组件显示可以随时间变化的数据时,我们希望尽快显示变化。例如,考虑 AutoT0:Up 组件:它显示了包含在 Type T1 数组中的产品列表。如果将新产品添加到阵列中,我们希望立即显示它。React 提供了一种机制,支持在数据更改时自动呈现组件。这种机制基于状态的概念。

React state is a property that represents data that changes over time. Every component supports the state property, but it should be used carefully.

再考虑一下,Ty0T0.组件:

import React from 'react';
import Product from './Product';

class ProductList extends React.Component {
  render() {
    let products = [
      {code:"P01", name: "Traditional Merlot", description: "A bottle
       of middle weight wine, lower in tannins (smoother), with a more 
       red-fruited flavor profile."},
      {code:"P02", name: "Classic Chianti", description: "A medium-bodied
       wine characterized by a marvelous freshness with a lingering, 
       fruity finish"},
      {code:"P03", name: "Chardonnay", description: "A dry full-bodied 
       white wine with spicy, bourbon-y notes in an elegant bottle"},
      {code:"P04", name: "Brunello di Montalcino", description: "A bottle
       of red wine with exceptionally bold fruit flavors, high tannin, 
       and high acidity"}
    ];

    let productComponents = [];

    for (let product of products) {
      productComponents.push(<Product item={product}/>);
    }

    return <ul>{productComponents}</ul>;
  }
}

export default ProductList;

从实际的角度来看,它没有那么有用。它显示产品的硬编码列表。如果我们想添加新产品,我们需要对组件源代码进行更改。

在真实场景中,我们希望使组件的代码独立于产品数据。例如,我们可以通过向 web 服务器发出 HTTP 请求来获取产品数据。在这种情况下,products数组将表示随时间变化的数据:最初是一个空数组,然后用从服务器接收到的产品数据填充该数组,随后向服务器发出的请求将再次对其进行更改。

存储随时间变化的数据的组件称为有状态组件。有状态组件将状态存储在this.state属性中。要通知组件状态已更改,必须使用setState()方法。此方法为组件设置新状态;它不会更新它。状态的更改触发组件的渲染;也就是说,render()方法的自动执行。

让我们看看如何通过更改ProductList组件定义来管理状态:

import React from 'react';
import Product from './Product';

class ProductList extends React.Component {
  constructor() {
 super();
 this.state = { products: [] };

 fetch("products.json")
 .then(response => response.json())
 .then(json => {this.setState({products: json})})
 .catch(error => console.log(error));
 }

  render() {
    let productComponents = [];

    for (let product of this.state.products) {
      productComponents.push(<Product item={product}/>);
    }
    return <ul>{productComponents}</ul>;
  }
}
export default ProductList;

我们将构造函数添加到组件中。构造函数运行超类构造函数,并将组件的初始状态设置为具有空数组的products属性的对象。

然后,通过fetch()向服务器发送 GET HTTP 请求。由于请求是异步的,因此组件的初始呈现将是产品的空列表。

State initialization is the only case where you can assign a value to the this.state property without using setState().

当收到 HTTP 响应时,用setState()改变组件的状态。此状态更改导致自动执行render(),将显示从服务器接收的产品列表。

现在我们知道了如何管理组件的状态,在使用setState()方法时需要记住以下几点:

  • setState()将新数据与状态中已包含的旧数据合并,并覆盖以前的状态
  • setState()触发render()方法的执行,所以千万不要显式调用render()

组件状态管理似乎非常简单。然而,在决定什么应该被视为状态以及哪个组件应该是有状态的时,很容易遇到麻烦。

以下是关于州政府的一些建议:

  • 状态应包含表示 UI 中随时间变化的数据所需的最小数据;从这个最小数据中得到的任何信息都应该在render()方法中进行计算
  • 应尽可能避免状态,因为它会增加组件的复杂性
  • 有状态组件应位于 UI 的组件层次结构的较高位置

我们可以考虑最后一条建议,这是第二条建议的结果。如果我们应该限制状态的使用,我们应该减少有状态组件的数量。因此,将有状态组件的角色分配给作为用户界面中组件层次结构根的组件是一个很好的规则。您还记得我们在上一章中讨论的组件分类为表示组件和容器组件吗?通常,容器组件是有状态组件的良好候选对象。

在我们的示例应用程序中,我们将有状态组件的角色分配给ProductList组件。即使它是一个容器组件,它也不是应用程序的组件层次结构中最高的。也许这个角色更适合Catalog组件。在这种情况下,我们应该将获取数据的逻辑移到Catalog组件内部,如下代码所示:

import React from 'react';
import './Catalog.css';
import ProductList from './ProductList';

class Catalog extends React.Component {
  constructor() {
    super();
    this.state = { products: [] };

    fetch("products.json")
      .then(response => response.json())
      .then(json => {this.setState({products: json})})
      .catch(error => console.log(error));
  }

  render() {
    return <div><h2>Wine Catalog</h2><ProductList 
 items={this.state.products}/></div>;
  }
}

export default Catalog;

You can find a project ready in the my-shop-05 folder at Code/Chapter-2.

活动:向购物车组件添加状态管理

场景

为了使Cart组件生产就绪,我们添加了状态管理和动态数据加载。

瞄准

本活动的目的是熟悉组件状态管理。

完成步骤

将先前创建的Cart组件更改为添加状态管理,以便通过 HTTP 请求加载数据,并自动更新购物车的内容。

溶液

一种可能的解决方案是包含在Code/Chapter-2/my-cart-04文件夹中的解决方案。

总结

在本章中,我们开始创建 React 组件并探索其基本特性。特别是,我们:

  • 学习了如何将组件定义为从React.Component派生的类,以及如何导入特定的 CSS 样式
  • 探索了 JSX 的语法,它允许我们快速定义组件的图形方面,并使用在别处定义的 React 组件
  • 组合反应组件以构建其他组件
  • 使用状态管理功能,以便 React 组件在数据更改时自动更新其可视化表示

在下一章中,我们将分析如何使用基于 React 的应用程序管理用户交互;换句话说,就是如何捕获事件并使 UI 对这些事件做出反应。