如何编写你的第一个 React.js 组件
本文是我在 Pluralsight 课程React.js:入门的部分内容 。我在那里以视频形式介绍了类似的内容。
在 React.js 中,最需要理解的概念是组件。React 组件可以分为两种类型:函数组件和类组件。有时你会听到不同的术语来描述这两种类型,例如无状态组件和有状态组件。函数组件也经常与表示概念相关联。在本文中,我将它们分别称为函数组件和类组件。
函数组件是 React 组件最简单的形式。它是一个具有简单契约的简单函数:

截图来自我的 Pluralsight 课程 — React.js:入门
函数组件接收一个属性对象,该对象通常名为props。它返回的内容看起来像 HTML,但实际上是一种称为JSX 的特殊 JavaScript 语法。
类组件是定义 React 组件的一种更具特色的方式。它的作用类似于接收 props 的函数,但该函数还将私有的内部状态视为控制返回 JSX 的附加输入。

截图来自我的 Pluralsight 课程 — React.js:入门
这种私有的内部状态赋予了 React响应式的特性。当类组件的状态发生变化时,React 会在浏览器中重新渲染该组件。
State 对象和 Props 对象有一个重要的区别。在类组件内部,State 对象可以更改,而 Props 对象表示固定值。类组件只能更改其内部状态,而不能更改其属性。这是理解 React 的核心思想,本文将通过一个示例来说明。
我们来看一个组件的实际示例。一个非常简单的组件,没有任何输入,只有一个简单的h1输出div。

截图来自我的 Pluralsight 课程 — React.js:入门
左侧,组件是用特殊的 JSX 语法编写的。
JSX 允许我们使用非常接近 HTML 的语法来描述用户界面 (UI)。然而,它是可选的。React 可以在没有 JSX 的情况下使用,如您在右侧所见。实际上,React 只是将您在左侧看到的 JSX 编译为您在右侧看到的纯 JavaScript。然后,它就可以在浏览器中使用编译后的 JavaScript 了。
React.createElement右侧的调用是文档对象模型(DOM)的 JavaScript 表示。React高效地将其转换为在浏览器中执行的 DOM 操作。
让我们编写一个 React 组件。
我将使用 jsComplete 的React Playground作为本文的示例。它是一个可以直接在浏览器中测试 JavaScript 和 React 代码的工具。无需安装或配置任何东西。
该工具拥有简洁的双面板界面。左侧面板是编辑器,您可以在其中编写 JavaScript 和 React 代码。React 和 ReactDOM 的最新版本都已预装在编辑器中。编辑器还能识别 JSX 扩展和 JavaScript 中的所有现代特性。这使得我们可以专注于 React API 本身,而不是配置和编译 React 应用程序。
右侧面板是预览面板。编辑器中有一个预定义mountNode元素。执行 JavaScript 代码时,您在mountNode元素中输入的任何内容都会显示在预览面板中。预览面板还会显示您在执行代码时遇到的任何错误。这个 Playground 也是一个简单的 JavaScript REPL(运行、求值、打印、循环),您可以在其中测试快速的 JavaScript 函数和表达式。要随时执行代码,请按CTRL+Enter。
例如,尝试以下操作:
mountNode.innerHTML = 'Hello!!';
或者简单的 REPL 模式
3 == '3'
要创建 React 组件,请定义一个新函数。让我们让该函数返回一个 HTML 按钮元素:
function Button() {
return (
<button>Go</button>
);
}
我们在这里返回的内容看起来像 HTML,但请记住它不是。它将被编译成 JavaScript。当我们在 JSX 中使用此按钮元素时,浏览器实际看到的 JavaScript 是对以下React.createElement函数的调用:
function Button() {
return (
React.createElement("button", null, "Go")
);
}
虽然你也可以不使用 JSX 来以这种方式使用 React,但编码和维护起来会困难得多。所以,我们还是坚持使用 JSX 吧。
上面的函数是一个完整且非常简单的 React 组件。让我们使用它吧!
我们通过将组件挂载到浏览器中来使用它。为此设计的函数是ReactDOM.render,它接受两个参数:
- 第一个是要渲染的组件,在我们的例子中是
Button。 - 第二个参数是该组件应该渲染到哪个元素中。在 REPL 的环境中,我们可以使用特殊
mountNode变量。
ReactDOM.render(<Button />, mountNode);
本文中的所有代码示例在屏幕截图标题中都有一个链接,您可以在 jsComplete REPL 上编辑示例。
React 函数组件的第一个参数是该props对象。这个参数允许我们让组件可复用。例如,我们不需要对上面按钮的“Go”标签进行硬编码,而是可以像处理普通 HTML 元素一样,给Button组件传递一个label属性:
ReactDOM.render(<Button label="Save" />, mountNode);
然后我们可以使用花括号在组件内部访问此属性props.label。
function Button(props) {
return (
<button>{props.label}</button>
);
}
参数props是一个对象,它保存了渲染组件时传递给组件的所有值。
使组件具有交互性
我们有一个按钮元素,它是通过 React 组件呈现的。
现在让我们为这个目前为止比较枯燥的示例添加一些交互功能。让我们让按钮元素在每次点击时增加一个计数器值,并将该值显示为按钮本身的标签。因此,这个按钮的标签将从数字 1 开始,当用户点击按钮时,它的标签将变为 2、3、4,依此类推。
由于这需要在组件渲染输出中体现,因此它属于组件的状态。我们需要组件在每次计数器更改时重新渲染自身。我们不能在这里使用属性,因为组件的 props 无法更改。通过使用特殊的 React 状态对象,我们将利用 React 的响应式特性,无需担心如何将更改传递到浏览器。React 会为我们完成这项工作。
但是,我们的 Button 组件目前是一个函数组件。函数组件无法拥有状态,所以我们需要先将其升级为类组件。
这很简单。我们首先定义一个扩展类React.Component
class Button extends React.Component { }
在该类中,我们定义了一个render函数,它返回组件的 JSX;在我们的例子中是 HTML 按钮。
render() {
return (
<button>1</button>
);
}
这是更多的代码,但是我们现在可以在 Button 组件上使用私有状态!
要使用状态对象,首先需要初始化它。状态对象是一个简单的实例属性,因此我们可以在Button类的构造函数中初始化它。我们只需定义一个普通的构造函数(在 React 中接收一个props对象)并调用该super方法即可,以遵循组件的继承关系。
constructor(props) {
super(props);
this.state = { counter: 1 };
}
之后,我们将其初始化this.state为所需的值。此状态对象的键是状态的各个元素。对于我们的情况,我们需要一个counter从 1 开始的状态。
counter在渲染函数内部,由于我们可以在花括号内编写任何 JavaScript 表达式,因此我们可以读取使用在状态上初始化的新状态元素的值this.state.counter。
render() {
return (
<button>{this.state.counter}</button>
);
}
“ this”关键字指的是我们要传递的组件实例ReactDOM。
您可以尝试更改该计数器状态,以查看按钮如何呈现您在该状态上输入的值。
还有另一种更短的语法来定义初始状态,即简单地使用类属性而不调用构造函数:
class Button extends React.Component {
state = { counter: 1 };
render() {
return (
<button>{this.state.counter}</button>
);
}
}
这还不是官方 JavaScript 语言的一部分,但很快就会成为官方 JavaScript 语言。该语法在 jsComplele REPL 环境中有效,因为该工具使用 Babel 将其转换为浏览器可以理解的 JavaScript。
当你配置自己的 React 应用程序时,无论如何都必须使用类似 Babel 的工具将 JSX 编译成 JavaScript。同时,包含并使用即将成为该语言正式组成部分的 JavaScript 特性也是一件轻而易举的事。
在目前的示例中Button,我们有一个状态对象和一个 HTML 按钮元素,该按钮元素显示我们在状态上初始化的计数器值。现在我们需要在点击按钮时更改该值。我们需要在该按钮上定义一个点击处理程序。
React 自带了易于使用的规范化事件。在本例中,我们需要onClick在 HTML 按钮元素上定义事件:
function F() {}
<button onClick={F} />
与使用字符串的 DOM 事件处理程序不同,React 事件处理程序使用实际的 JavaScript 函数。此函数可以是全局函数(F如上所示),也可以是内联函数:
<button onClick={() => {}} />
但是,标准做法是在类组件本身上定义一个函数。handleClick我们可以在组件上将其定义为实例属性来调用它:
class Button extends React.Component {
state = { counter: 1 };
handleClick = () => {
console.log('Button is clicked!!');
};
render() {
return (
<button onClick={this.handleClick}> {this.state.counter} </button>
);
}
}
我们使用的是现代的类字段语法,它允许我们使用绑定到组件实例的箭头函数。handleClick现在将充当此类的原型函数。handleClick关键字“ this”指的是我们正在 DOM 中挂载的组件实例。
handleClick的工作很简单:使用 从状态对象中读取当前计数器值this.state.counter。然后增加此值,并用新的增加值更新组件状态。
我们可以使用 React 的内置setState方法(每个类组件实例上都有)来更新组件状态。
现在,每次点击按钮时,其标签都会增加。
这很简单,但功能强大!我们为该onClick方法定义了一个事件处理程序。每次用户点击按钮时,该handleClick函数都会被执行。该函数读取计数器值的当前状态,将其递增,然后将状态设置为新的递增值。React 会负责这些更改后所需的所有渲染,因此您无需担心。
请注意,我们没有直接更新状态对象。当我们想要更新状态中的任何元素时,必须使用 React 的setState方法。例如,你不能这样做:
// WRONG:
this.state.counter = this.state.counter + 1;
React 的setState方法是异步的,它会安排更新。setState为了提高性能,可能会将多个调用分批处理。由于我们在handleClick函数内部同时读取和写入状态对象,因此可能会遇到竞争条件。一般的经验法则是,每当您需要使用当前状态的值更新状态时,请使用该setState方法的另一个契约。它接收一个函数引用而不是对象作为其第一个参数:
this.setState((prevState) => {});
这个函数接收一个prevState我们可以放心使用的对象,不用担心竞争条件。该函数返回我们希望 React 用来设置状态的对象。我们counter上面的值示例变成了:
this.setState((prevState) => ({
counter: prevState.counter + 1
}));
只有当更新依赖于当前状态时,才需要使用第二种语法setState。不过,养成始终使用第二种函数参数语法的习惯可能是一个好主意。
最终代码如下:
class Button extends React.Component {
state = { counter: 1 };
handleClick = () => {
this.setState((prevState) => ({
counter: prevState.counter + 1
}));
};
render() {
return (
<button onClick={this.handleClick}>
{thi
s.state.counter}
</button>
);
}
}
ReactDOM.render(<Button />, mountNode);
测试一下,如果有任何问题请告诉我。
还在学习 React 还是 Node?看看我的书:
本文最初发表于此处
文章来源:https://dev.to/samerbuna/how-to-write-your-first-reactjs-component-191i
后端开发教程 - Java、Spring Boot 实战 - msg200.com





