React入门教程 – 概述和实际演练

10年服务1亿前端开发工程师

本文概述和实际演练 React 的基本概念,例如组件,state(状态) 和 props(属性),以及提交表单,从 API 获取数据以及将 React 应用程序部署到生产环境。

自从我第一次开始学习 JavaScript 以来,我一直都有听说 React ,但我承认我只粗略的看了一下 React 相关内容就被吓到了。我看到一堆 HTML 和 JavaScript 混合在一起的代码,我想,这不是我们一直试图避免的吗? React 有什么大不了的?

相反,我只专注于学习原生的 JavaScript 并在专业环境中使用 jQuery 。在经历了几次失败的尝试之后,我终于开始尝试使用 React ,我开始明白了为什么我要使用 React 而不是原生的 JS 或 jQuery

我试着将我学到的所有内容浓缩成这篇很好的介绍文章,并与你分享。

预备知识

在开始使用 React 之前,您应该事先知道一些事情。 例如,如果您以前从未使用过 JavaScript 或 DOM ,那么在尝试使用 React 问题之前,你应该首先熟悉它们。

以下是我认为是React的先决条件。

  • 基本熟悉 HTML 和 CSS
  • JavaScript 基础知识和编程思想。
  • 对 DOM 有基本的了解。
  • 熟悉 ES6 语法和功能
  • Node.js 和 npm 全局安装。

目标

  • 了解必要的 React 概念和相关术语,例如 Babel,Webpack,JSX,components(组件),props(属性),state(状态) 和 生命周期。
  • 构建一个非常简单的 React 应用程序,实际演练上述概念。

本文 实际演练的源代码 已经放到了GitHub上。

什么是 React ?

  • React 是一个 JavaScript 库 – 最受欢迎的库之一,在 GitHub 上有超过 100,000 stars
  • React 不是一个框架(与 Angular 不同,后者更 opinionated (倾向性))。
  • React 是一个由 Facebook 创建的开源项目。
  • React 用于在前端构建用户界面(UI)。
  • React 是 MVC(Model View Controller,模型视图控制) 应用程序的 视图 层。

React 最重要的一个方面是,您可以创建 components(组件),类似于自定义,可重用的HTML元素,可以快速高效地构建用户界面。 React 还利用 state(状态) 和 props(属性) 简化了数据的存储和处理方式。

我们将在整篇文章中详细介绍所有这些内容,让我们开始吧。

设置和安装

设置 React 有几种方法,我会在这里告诉你种方法,这样你就可以很好地了解它是如何工作的。

静态HTML文件

第一种方法不是设置 React 的流行方式,本教程的其余部分也不是使用这个方法的,但是如果你曾经使用像 jQuery 这样的库,那么这种你将会很熟悉且易于理解,如果你不熟悉 Webpack ,Babel 和 Node.js ,那么可以使用这种最简单的方法开始。

让我们从制作一个基本的 index.html 文件开始。 我们将在头部加载三个 CDN 文件- React,React DOM 和 Babel 。 我们还将创建一个 id 为 rootdiv 元素,最后我们将创建一个 script 标签,您的自定义代码将都放在这里。

<!doctype html>
<html>

<head>
    <meta charset="utf-8">

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
</head>

<body>

    <div id="root"></div>

    <script type="text/babel">
        // React 代码将放在这里
    </script>

</body>

</html>

在撰写本文时,我加载的是最新的稳定版本的 React 。

我们的应用程序的入口点将是 id 为 rootdiv 元素,它按惯例命名。 您还会注意到 text/babel 脚本类型,这对于使用 Babel 是必需的。

现在,让我们编写我们的第一个 React 代码块。 我们将使用 ES6 类来创建一个名为 App 的 React 组件。

class App extends React.Component {
   //...
}

现在我们将添加 render() 方法,这是类组件中唯一必须的方法,用于渲染 DOM 节点。

class App extends React.Component {
    render() { 
        return (
           //...
        ); 
    } 
}

return 内部 ,看起来像一个简单的 HTML 元素。请注意,我们不需要在此处返回 String 类型的字符串,因此请不要使用引号将元素括起来。这里的内容被称为 JSX ,我们很快将在下面的文章中了解它。 (注:这里是 JSX 的中文文档,有兴趣你可以先了解一下。)

class App extends React.Component {
    render() { 
        return (
           <h1>Hello React!</h1>
        ); 
    } 
}

最后,我们将使用 React DOM 的 render() 方法将我们创建的 App 类渲染到 HTML 中的 id 为 rootdiv 元素中。

ReactDOM.render(<App />, document.getElementById('root'));

这是 index.html 的完整代码。

<!doctype html>
<html>

<head>
    <meta charset="utf-8">

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
</head>

<body>

    <div id="root"></div>

    <script type="text/babel">
        class App extends React.Component { 
            render() { 
                return (
                    <h1>Hello world!</h1>
                ); 
            } 
        } 

        ReactDOM.render(<App />, document.getElementById('root'));
    </script>

</body>

</html>

现在,如果您在浏览器中查看 index.html ,您将看到我们创建的 h1 标签已经渲染到 DOM 中。

我们创建的 <code/>h1 标签已经渲染到 DOM 中” /></p>
<p>酷!既然你已经完成了这个,你可以看到开始使用 React 并不是那么糟糕是不是? 它只是一些我们可以加载到 HTML 中的 JavaScript 帮助程序库。</p>
<p>我们这样做是出于演示的目的,但从现在开始我们将使用另一种方法:Create React App 。</p>
<h3>Create React App</h3>
<p>我刚刚将 React 库和 Babel 加载到静态 HTML 页面中,并动态渲染,但是我们发现那种方法效率不高,而且难以维护。</p>
<p>幸运的是,Facebook 创建了 <a href=Create React App ,这是一个 React 开发环境,预先配置了构建 React 应用程序所需的一切。 它将创建一个实时开发服务器,使用 Webpack 自动编译 React,JSX 和 ES6,自动加载 CSS 文件,并使用 ESLint 测试和警告代码中的错误。

要设置 create-react-app ,请在项目所在目录的终端中运行以下代码。 请确保 Node.js 5.2或更高版本。

npx create-react-app react-tutorial

完成安装后,转移到新创建的目录并启动项目。

cd react-tutorial
npm start

此命令运行后,浏览器将在一个新窗口中打开新的 React 应用程序,网址为: localhost:3000

用 Create React App 创建 React 应用

如果查看项目结构,您将看到 /public/src 目录,以及常规的 node_modules.gitignoreREADME.mdpackage.json

/public 中,重要文件是 index.html,它与我们之前制作的静态 index.html 文件非常相似 – 只是一个 root div。 在这里,index.html 文件中没有加载任何库或脚本。/src 目录将包含我们所有的 React 代码。

要查看环境如何自动编译和更新您的 React 代码,请在 /src/App.js 中找到如下所示的行:

To get started, edit src/App.js and save to reload.

将它替换为任何其他文本,保存文件后,您会注意到 localhost:3000 将使用新数据进行编译,并且自动刷新。

继续并删除 /src 目录中的所有文件,我们将创建自己的样板文件。为了不使项目膨胀,我们将只保留index.cssindex.js

对于 index.css,我只是将 Primitive CSS 的内容复制并粘贴到文件中。 如果需要,可以使用 Bootstrap 或任何您想要的 CSS 框架,或者根本不使用。 我发现它更容易使用。

现在在 index.js 中,我们正在导入 React,ReactDOM 和 CSS 文件。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

让我们再次创建我们的 App 组件。 之前,我们只有一个 <h1> 标签,但现在我在一个div元素中加入了一个样式类。 您会注意到我们使用 className 而不是 class。 这是我们第一次暗示这里编写的代码是JavaScript,而不是 HTML 。

class App extends Component {
    render() {
        return (
            <div className="App">
                <h1>Hello, React!</h1>
            </div>
        );
    }
}

最后,我们将像以前一样将应用程序渲染到 root div 中。

ReactDOM.render(<App />, document.getElementById('root'));

这是我们的完整 index.js 。这次,我们将 Component 作为 React 的 property(属性) 加载,因此我们不再需要扩展 React.Component

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';

class App extends Component {
    render() {
        return (
            <div className="App">
                <h1>Hello, React!</h1>
            </div>
        );
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

如果你回到浏览器 localhost:3000 ,就会和之前一样看到 “Hello, React!” 。我们现在就已经开始使用 React 应用程序了。

想要了解更多的设置和安装方法,可以查看 React 中文文档 创建一个新的 React APP将 React 添加到网站 中介绍。

React 开发人员工具

React Developer Tools 扩展可以让您在使用 React 时更轻松。 下载适用于 Chrome 的React DevTools 或您喜欢的任何浏览器。

安装后,当您打开开发人员工具时,您将看到 React 的选项卡。 单击它,您将能够在编写组件时对其进行检查。 您仍然可以转到 Elements 选项卡以查看实际的 DOM 输出。 现在看起来似乎不太合适,但随着应用程序变得越来越复杂,使用它将变得越来越必要。

React Developer Tools 扩展

现在我们拥有了实际开始使用 React 所需的所有工具和设置了。

JSX: JavaScript + XML

正如您所见,我们一直在 React 代码中使用类似于 HTML 的东西,但它不是 HTML 。 这是 JSX,代表JavaScript XML。

使用JSX,我们可以编写看起来像HTML的内容,也可以创建和使用我们自己的类似xml的标记。下面是分配给变量的JSX。

const heading = <h1 className="site-heading">Hello, React</h1>;

React 没有强制我们使用 JSX 。 在底层,它其实是在执行 createElement,它接受标签、包含属性的对象和组件的子元素,并渲染相同的信息。下面的代码将具有与上面的JSX相同的输出。

const heading = React.createElement(
    'h1',
    {className: 'site-heading'},
    'Hello, React!'
);

JSX 实际上更接近 JavaScript ,而不是 HTML ,因此在编写时需要注意几个关键的区别。

  • 使用 className 代替 class 来添加CSS类,因为 class 是 JavaScript 中的保留关键字。
  • JSX 中的属性和方法是 camelCase(驼峰命名) – onclick 将写成 onClick
  • 自动闭合标签必须以斜线结尾 – 例如 <img />

JavaScript表达式也可以使用花括号嵌入到 JSX 中,包括变量,函数和属性。

const name = 'Tania';
const heading = <h1>Hello, {name}</h1> ;

JSX 比在原生的 JavaScript 中创建和附加多个元素更容易编写和理解,这也是人们喜欢React的原因之一。

更多信息请查看 React 中文文档中的 JSX 介绍深入 JSX

组件(Components)

到目前为止,我们已经创建了一个组件– App 组件。 React 中的几乎所有内容都由组件组成,组件可以是 class components(类组件)或 simple components(简单组件,注普遍的叫法为 functional components ,也就是函数式组件)。

大多数 React 应用程序都有许多小组件,所有内容都加载到主 App 组件中。 组件也经常有自己的文件,所以来修改一下我们的项目试试。

index.js 中删除 App 类,所以它看起来像这样。

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

ReactDOM.render(<App />, document.getElementById('root'));

我们将在 src 目录中创建一个名为 App.js 的新文件,并将组件放置以下代码。

import React, {Component} from 'react';

class App extends Component {
    render() {
        return (
            <div className="App">
                <h1>Hello, React!</h1>
            </div>
        );
    }
}

export default App;

我们将组件 export(导出) 为 App 并将其加载到 index.js 中。将组件分离到单独文件中并不是强制的,但如果不这样做,应用程序将开始变得笨拙,达到一定程度将难以维护,甚至失控。

类组件(Class Components)

让我们来创建另一个组件。 我们要创建一个表格,命名为 Table.js,并用以下数据填充它。

import React, {Component} from 'react';

class Table extends Component {
    render() {
        return (
            <table>
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Job</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>Charlie</td>
                        <td>Janitor</td>
                    </tr>
                    <tr>
                        <td>Mac</td>
                        <td>Bouncer</td>
                    </tr>
                    <tr>
                        <td>Dee</td>
                        <td>Aspiring actress</td>
                    </tr>
                    <tr>
                        <td>Dennis</td>
                        <td>Bartender</td>
                    </tr>
                </tbody>
            </table>
        );
    }
}

export default Table;

我们创建的这个组件是一个自定义 class components(类组件)。 我们将自定义组件的首字母大写,以区别于常规 HTML 元素。 回到 App.js ,我们可以用 Table 名称来加载她,首先 import(导入) 它:

import Table from './Table';

然后将它加载到 Apprender() 方法中,也就是把之前的 “Hello, React!” 替换掉。我还修改了外容器的样式类 container

return (
    <div className="container">
        <Table />
    </div>
);

如果您重新在浏览器中查看,则会看到 Table 已经被加载进来了。

Table组件已经被加载进来了

现在我们已经看到了自定义类组件是什么。我们可以一遍又一遍地重用这个组件。但是,由于数据是硬编码的,因此目前不太好用。

函数式组件(Functional Components)

注:原文中的叫法是 简单组件(Simple Components),翻译的时候用了官方的叫法 functional components ,也就是函数式组件。可以查看官方文档《组件(Components) 和 属性(Props)》 中关于 函数式组件和类组件 的描述。

React 中的另一种组件是 functional components(函数式组件),它是一个函数。这种组件不使用 class 关键字。 让我们为 Table 组件制作两个函数式组件 – table header(表头) 和 table body(表体) 。

我们将使用 ES6 箭头函数来创建这些简单组件。 首先是表头:

const TableHeader = () => { 
    return (
        <thead>
            <tr>
                <th>Name</th>
                <th>Job</th>
            </tr>
        </thead>
    );
}

然后是表体:

const TableBody = () => { 
    return (
        <tbody>
            <tr>
                <td>Charlie</td>
                <td>Janitor</td>
            </tr>
            <tr>
                <td>Mac</td>
                <td>Bouncer</td>
            </tr>
            <tr>
                <td>Dee</td>
                <td>Aspiring actress</td>
            </tr>
            <tr>
                <td>Dennis</td>
                <td>Bartender</td>
            </tr>
        </tbody>
    );
}

现在我们的 Table 类看起来像这样。

class Table extends Component {
    render() {
        return (
            <table>
                <TableHeader />
                <TableBody />
            </table>
        );
    }
}

一切都应该像以前一样出现。如您所见,组件可以嵌套在其他组件中,并且可以混合函数式组件和类组件。

注意:类组件必须包含 render() 方法,并且 return 只能返回一个父元素。

我们来比较一下 functional components(函数式组件)和 class components(类组件)。

functional components(函数式组件):

const SimpleComponent = () => { 
    return <div>Example</div>;
}

class components(类组件)

class ClassComponent extends Component {
    render() {
        return <div>Example</div>;
    }
}

请注意,如果 return 内容包含在一行中,则不需要用花括号括起来了。

了解关于组件的更多信息请查看官方文档《组件(Components) 和 属性(Props)》(中文)。

Props(属性)

现在,我们有了一个很好的 Table 组件,但数据是硬编码的。 React 最重要的特性之一就是处理数据的方式,它使用 props(属性) 和 state(状态) 来实现。首先,我们将重点讨论如何使用属性(props)来处理数据。

首先,让我们从 TableBody 组件中删除所有数据。

const TableBody = () => { 
    return <tbody></tbody>;
}

然后让我们将所有数据移动到一个由对象组成的数组中,就好像我们引入了一个基于 JSON 的 API 一样。我们必须在 render() 中创建这个数组。

class App extends Component {
    render() {
        const characters = [
            {
                'name': 'Charlie',
                'job': 'Janitor'
            },
            {
                'name': 'Mac',
                'job': 'Bouncer'
            },
            {
                'name': 'Dee',
                'job': 'Aspring actress'
            },
            {
                'name': 'Dennis',
                'job': 'Bartender'
            }
        ];

        return (
            <div className="container">
                <Table />
            </div>
        );
    }
}

现在,我们将使用属性将数据传递给子组件( Table ),以及如何使用 data- 属性传递数据。 我们可以随意使用属性名称,只要它不是保留关键字,所以我将使用 characterData 。 我传递的数据是 characters 变量,我会在它使用花括号括起来,因为它是一个 JavaScript 表达式。

return (
    <div className="container">
        <Table characterData={characters} />
    </div>
);

现在我们将数据传递给了 Table,我们必须在Table组件中访问它。

class Table extends Component {
    render() {
        const { characterData } = this.props;

        return (
            <table>
                <TableHeader />
                <TableBody characterData={characterData} />
            </table>
        );
    }
}

如果打开 React DevTools 并检查 Table 组件,您将在属性中看到数据数组。这里存储的数据称为 virtual DOM(虚拟DOM) ,这是一种快速有效的方法,可以将数据与实际 DOM 同步。

virtual DOM(虚拟DOM)

但是,这些数据还没有被渲染到实际的 DOM 中。 在 Table 中,我们可以通过 this.props 访问所有 props(属性)。 我们通过 characterData 只传递了一个 props(属性),所以我们将使用 this.props.characterData 来读取该数据。

我将使用 ES6 属性简写的方式来创建包含 this.props.characterData 的变量。

const { characterData } = this.props;

由于我们的 Table 组件实际上由两个较小的函数式组件组成,因此我将再次通过 props(属性) 将数据传递给 TableBody

class Table extends Component {
    render() {
        const { characterData } = this.props;

        return (
            <table>
                <TableHeader />
                <TableBody characterData={characterData} />
            </table>
        );
    }
}

现在,TableBody 不带参数并返回一个单独的标签。

const TableBody = () => { 
    return <tbody></tbody>;
}

我们将把 props(属性) 作为参数传递,并通过数组映射 以返回数组中每个对象的 table row (表行)。这个映射将包含在 row 变量中,我们将以表达式的形式返回这个变量。

const TableBody = props => { 
    const rows = props.characterData.map((row, index) => {
        return (
            <tr key={index}>
                <td>{row.name}</td>
                <td>{row.job}</td>
            </tr>
        );
    });

    return <tbody>{rows}</tbody>;
}

如果你在浏览器中查看应用程序,则现在已经加载了所有数据。

您会注意到我已经为每个 table row(表行) 添加了一个键索引。 在 React 中创建列表时应始终使用 keys ,因为它们有助于识别每个列表项。 当我们想要操作列表项时,我们还将看到这一点有多么的重要。

props(属性) 是将现有数据传递给 React 组件的有效方法,但是组件不能更改 props(属性) – 它们是只读的。 在下一节中,我们将学习如何使用 state(状态) 来进一步控制 React 中的数据处理。

查看官方文档《组件(Components) 和 属性(Props)》(中文) 和 《列表(Lists) 和 键(Keys)》以了解更多本章节中介绍的内容。

State(状态)

现在,我们将 character 数据以数组的形式存储在变量中,并将其作为 props(属性) 传递。 这是很好的开始,但想象一下,我们是否希望能够从数组中删除一项元素。 使用 props(属性) ,我们有单向数据流,但有了 state(状态),我们可以更新组件中的私有数据。

您可以将 state(状态) 视为任何应该保存和修改的数据,而不必添加到数据库中,例如,在确认购买之前从购物车中添加和删除商品。

首先,我们将创建一个 state(状态) 对象。

class App extends Component {
    state = {};

该对象将包含您要在 state(状态) 中存储的所有内容的属性。对于这个例子来说,是 characters

class App extends Component {
    state = {
        characters: []        
    }; 

将我们之前创建的整个对象数组移到 state.characters 中。

class App extends Component {
    state = {
        characters: [
            {
                'name': 'Charlie',
                // the rest of the data
        ]        
    };

我们的数据正式包含在 state(状态) 中了。 由于我们希望能够从表中删除一个人物(一条记录),因此我们将在父 App 类上创建一个 removeCharacter 方法。

要检索 state(状态) ,我们将使用与以前相同的 ES6 方法获取 this.state.characters 。 要更新 state(状态) ,我们将使用 this.setState(),这是操作 state(状态) 的内置方法。 我们将根据我们传递的索引 过滤数组 ,并返回新数组。

注意:您必须使用 this.setState() 来修改数组。 简单地将一个新值应用于 this.state.property 将不起作用。

removeCharacter = index => {
    const { characters } = this.state;

    this.setState({
        characters: characters.filter((character, i) => { 
            return i !== index;
        })
    });
}

filter 不会改变原来的数组,而是创建一个新数组,并且是在 JavaScript 中修改数组的首选方法。 这个特殊的方法是测试索引与数组中的所有索引,并返回除了传递的索引之外的所有索引元素。

现在我们必须将该函数传递给组件,并在每个可以调用该函数的人物项旁边呈现一个按钮。 我们将 removeCharacter 函数作为 prop(属性) 传递给 Table

return (
    <div className="container">
        <Table
            characterData={characters}
            removeCharacter={this.removeCharacter} 
        />
    </div>
);

由于我们将它从 Table 传递给 TableBody ,我们将不得不再次将其作为 prop(属性) 传递,就像我们对 character(人物) 数据所做的那样。

class Table extends Component {
    render() {
        const { characterData, removeCharacter } = this.props;

        return (
            <table>
                <TableHeader />
                <TableBody 
                    characterData={characterData} 
                    removeCharacter={removeCharacter} 
                />
            </table>
        );
    }
}

这是我们在 removeCharacter() 方法中定义的索引的位置。在 TableBody 组件中,我们将 key/index(键/索引) 作为参数传递,因此 filter 函数知道要删除的是哪一项。 我们将创建一个带有 onClick 的按钮并将其传递给它。

<tr key={index}>
    <td>{row.name}</td>
    <td>{row.job}</td>
    <td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>

onClick 函数必须通过一个返回 removeCharacter() 方法的函数,否则它将尝试自动运行。

真棒。现在我们已经有了删除按钮,我们可以通过删除一个 character(人物) 来修改我们的 state(状态) 。

删除按钮

我删除了 Mac 。

现在您应该了解 state(状态) 是如何初始化的,以及如何修改 state(状态) 。

提交表格数据

现在我们将数据存储在 state(状态) 中,我们可以从 state(状态) 中删除任何数据项。 但是,如果我们希望能够向 state(状态) 添加新数据呢? 在现实的应用程序中,您更可能从空 state(状态) 开始,并将数据添加到 state(状态) 中,例如使用待办事项列表( to-do list) 或购物车。

在接下来我们要做的任何事情之前,让我们从 state.characters 中删除所有硬编码数据,因为我们接下来将通过表单更新 state(状态) 中的数据。

class App extends Component {
    state = {
        characters: []
    };

现在让我们继续,我们首先创建一个名为 Form.js 的新文件,在其中创建一个 Form 组件。 我们将创建一个类组件,并且我们将使用constructor(),这是我们到目前为止尚未接触到的。 我们需要 constructor() 来使用它,并接收父级的 props(属性) 。

我们将把 Form 的初始 state(状态) 设置为具有一些空属性的对象,并将该初始 state(状态) 分配给 this.state

import React, { Component } from 'react';

class Form extends Component {
    constructor(props) {
        super(props);

        this.initialState = {
            name: '',
            job: ''
        };

        this.state = this.initialState;
    }
}

我们对此表单的目标是每次在表单中更改字段时更新 Form 的 state(状态) ,并且当我们提交时,所有数据将传递到 App 的 state(状态) 中,然后将更新 Table 组件。

首先,我们将创建每次输入更改时运行的函数。 event 将被传递,我们将 Form 的 state(状态) 设置为输入的 name(键)和 value

handleChange = event => {
    const {name, value} = event.target;

    this.setState({
        [name] : value
    });
}

在我们继续提交表单之前,让我们先完成这个工作。在 render() 方法中,让我们从 state(状态) 中获取两个属性,并将它们指定为对应于适当形式键的值。我们将运行 handleChange() 方法作为 input 的 onChange ,最后 export(导出) Form 组件。

render() {
    const { name, job } = this.state; 

    return (
        <form>
            <label>Name</label>
            <input 
                type="text" 
                name="name" 
                value={name} 
                onChange={this.handleChange} />
            <label>Job</label>
            <input 
                type="text" 
                name="job" 
                value={job} 
                onChange={this.handleChange}/>
        </form>
    );
}

export default Form;

App.js 中,我们可以在表格下方渲染表单。

return (
    <div className="container">
        <Table
            characterData={characters}
            removeCharacter={this.removeCharacter}
        />
        <Form />
    </div>
);

现在,如果我们回到浏览器,我们会看到一个尚未提交的表单。更新一些字段,您将看到 Form 的本地 state(状态) 正在更新。

Form 的本地 state(状态) 正在更新

最后一步是允许我们实际提交该数据并更新父状态。 我们将在 App 上创建一个名为 handleSubmit() 的函数,该函数将使用现有的this.state.characters 并使用 ES6 展开运算符(ES6 spread operator 添加新的 character 参数来更新 state(状态) 。

handleSubmit = character => {
    this.setState({characters: [...this.state.characters, character]});
}

让我们确保我们把它作为一个参数传递给 Form

<Form handleSubmit={this.handleSubmit} />

现在在 Form 中,我们将创建一个名为 submitForm() 的方法,该方法将调用 handleSubmit 函数,并将 Form 的 state(状态) 作为我们之前定义的 character 参数传递。它还会将 state(状态) 重置为初始 state(状态) ,以便在提交后清除表单。

submitForm = () => {
    this.props.handleSubmit(this.state);
    this.setState(this.initialState);
}

最后,我们将添加一个提交按钮来提交表单。我们使用 onClick 而不是 onSubmit ,因为我们没有使用标准提交(submit)功能。点击将调用我们刚刚创建的 submitForm 方法。

 <input 
    type="button" 
    value="Submit" 
    onClick={this.submitForm} />

就是这样! 该应用程序已完成。 我们可以在表格中创建,添加和删除用户。 由于 TableTableBody 已经从 state(状态) 获取数据,它将正确显示。

应用程序已完成

更多关于 state(状态) 的信息请阅读 React 中文文档中的《状态(State) 和 生命周期

如果您在学习过程中有点迷茫或者混乱,可以在 GitHub上查看完整的源代码

引入API数据

React 应用开发过程中一个非常常见的用法是从 API 中获取数据。如果您不熟悉 API 是什么或者如何连接到 API ,我建议您阅读如何使用JavaScript连接到API,它将引导您了解 API 以及如何将它们与原生的 JavaScript 一起使用。

作为一个小测试,我们可以创建一个 Api.js 文件,并在那里创建一个新的App。我们可以测试的公共 API 是 Wikipedia API ,我在这里有一个 URL终端 用于 random(随机)*搜索。您可以转到该链接查看 API ,并确保在浏览器上安装了 JSONView

我们将使用 JavaScript 中内置的 Fetch 从该 URL 终端收集数据并显示它。您可以通过更改 index.js 中的URL,来切换我们创建的应用程序和此测试文件 – import App from './Api';

我不会逐行解释这个代码,因为我们已经学会了创建组件,渲染和映射 state(状态) 数组。此代码的新方面是 componentDidMount(),一个 React 生命周期方法。Lifecycle(生命周期) 是 React 中调用方法的顺序。Mounting(装载) 到DOM中的项。

当我们引入API数据时,我们希望使用 componentDidMount ,因为我们希望在引入数据之前确保组件已渲染到了 DOM 中。在下面的代码片段中,您将看到我们如何从Wikipedia API引入数据,并将其显示在页面上

import React, { Component } from 'react';

class App extends Component {
    state = {
        data: []
    };

    // Code is invoked after the component is mounted/inserted into the DOM tree.
    componentDidMount() {
        const url = "https://en.wikipedia.org/w/api.php?action=opensearch&search=Seona+Dancing&format=json&origin=*";

        fetch(url)
            .then(result => result.json())
            .then(result => {
                this.setState({
                    data: result
                })
            });
    }

    render() {
        const { data } = this.state;

        const result = data.map((entry, index) => {
            return <li key={index}>{entry}</li>;
        });

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

export default App;

在本地服务器中保存并运行此文件后,您将看到 DOM 中显示了来自 Wikipedia API 的数据。

React 从 API 中获取数据

还有其他生命周期方法,但重复这些方法将超出本文的范围。您可以在 React 中文文档中阅读《状态(State) 和 生命周期》,以及有关 React 组件的更多信息

构建和部署 React 应用程序

到目前为止,我们所做的一切都处于开发环境中。 我们一直在编译,热重新加载和更新。 对于生产环境,我们将要加载静态文件 – 没有源代码。 我们可以通过构建和部署它来实现这一目标。

现在,如果您只想编译所有 React 代码并将其放在某个目录的根目录中,那么您需要做的就是运行以下行:

npm run build

这将为你创建一个应用程序的 build 文件夹。 将该文件夹的内容放到任何地方,就没你什么事情了,轻松搞定!

我们还可以更进一步,让 npm 为我们部署。我们将构建到 GitHub 页面,因此您必须 熟悉Git 并在GitHub上安装代码。

确保您已退出本地 React 环境,因此代码当前未运行。 首先,我们将向 package.json 添加一个homepage字段,该字段包含我们希望我们的应用程序存在的URL。

"homepage": "https://taniarascia.github.io/react-tutorial",

我们还将这两行添加到 scripts 属性中。

"scripts": {
  // ...
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build"
}

在您的项目中,您将向devDependencies添加 gh-pages

npm install --save-dev gh-pages

我们将创建 build ,它将包含所有已编译的静态文件。

npm run build

最后,我们将部署到 gh-pages

npm run deploy

到这里我们的教程就结束了!该应用程序现已在 https://taniarascia.github.io/react-tutorial 上发布。

总结

本文为你介绍 React 的基本概念,包括函数式和类组件、state(状态)、props(属性)、处理表单数据,从一个 API 中获取,构建及部署一个应用程序。 React还有很多东西要学习和做,但我希望你现在能够自信地钻研 React。

如果有任何不清楚的地方,或者您希望在本文或后续文章中看到其他任何内容,欢迎留言告知我们。

推荐资源及文章

英文原文:https://www.taniarascia.com/getting-started-with-react/

赞(6) 打赏
未经允许不得转载:WEB前端开发 » React入门教程 – 概述和实际演练

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #-49

    state状态:在组件App添加state对象,把之前render里面的数组移到里面, 那render里面的数组不保留的话? 下面return报错:characters未找到? 这里没看明白
    class App extends Component {
    state = {
    characters: []
    };

    v_hfang2个月前 (11-13)回复
  2. #-48

    上述问题忽略,找到问题了,在render方法return里面需求这样写: characterData={this.state.characters}

    v_hfang2个月前 (11-13)回复

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏