如何编写你的第一个 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