react.js复用ui_React.js:更好地介绍了迄今为止创建的功能最强大的UI库。

react.js复用ui

先决条件:对HTML,JavaScript和CSS有所了解。

对React有更好的介绍吗?

不幸的是,那里的大多数React教程都没有考虑最佳实践,并且并不总是教会您“正确”的React方法。

在本教程中,我将介绍React的基础知识以及您可能会遇到的最常见的不良做法。

本教程将很长,因此请确保自己喝点咖啡!

为什么选择React?

在开始之前,我们先停下来看看为什么React确实是最佳选择。

陈述式

在React中,您描述要渲染的内容(而不是告诉浏览器该怎么做)。 这也意味着样板的数量大大减少了。

在React中,您只需开始编码,它就没有要生成的组件样板。 涉及到一些设置,但是对于组件,您可以将它们表示为纯函数。

语法清晰

React中的JSX就像HTML一样,没有特殊的语法需要学习:

学习曲线

选择UI框架时,学习曲线非常重要。 React的抽象最少。 如果您了解JavaScript,那么您可能可以在一天内开始编写React代码。 是的,选择最佳做法需要时间,但是您将能够非常快速地开始。

功能性

在我看来,React的最大优势在于您甚至没有被迫使用类。 类过于复杂,没有提供任何好处。

在React中,所有UI都可以表示为一组纯函数,并且使用纯函数来呈现UI就像呼吸新鲜空气一样。

让我们开始编码!

Fabian GrohsUnsplash发表的 文章“桌上灰色的iPhone 6s,黑色圆珠笔和黑色Asus无线光电鼠标旁边打开了MacBook Pro”

现在,我已经希望您说服您使用React,让我们来编写一些代码!

Node.js

Node.js是一个JavaScript运行时环境,使我们能够编译出色的React代码!

首先,让我们确保已安装Node.js。 如果没有,您可以从这里下载: https : //nodejs.org/en/download

创建React应用

我们将使用来自Facebook的create-react-app来搭建我们的应用程序。 这是设置环境并开始编码的最流行方法。 它带有许多必需的内置工具,可帮助我们消除许多决策。

要全局安装create-react-app:

npm i -g create-react-app

然后脚手架您的第一个项目运行:

create-react-app react-intro

就这样! 现在,启动应用程序:

cd react-intro
npm start

这将启动开发服务器,并允许您通过在浏览器中访问http:// localhost:3000 /来打开新的闪亮的React应用程序。

引擎盖下

现在,让我们来看看事物是如何运作的。 使用您选择的IDE(我建议使用Visual Studio Code )打开新创建的项目。

index.html

在项目内,转到public/index.html文件。 这是您将看到的:

我们感兴趣的部分是<div id="root"></div> 。 这是我们的React应用程序将要运行的地方。 整个root div将简单地替换为我们React应用程序的内容。 其他所有内容将保持不变。

index.js

现在打开src/index.js 。 这是引导整个React应用程序的文件。 顺便说一下,我们所有的React源代码都将进入src目录。

神奇的行是:

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

此行是告诉React使用我们的App组件的方法(我们将稍作讨论),并将其放置在index.html文件中上面定义的root div中。

现在让我们关注<App />部分。 这看起来很像HTML,不是吗? 这被称为JSX,是React用来实现其魔力的一种特殊JavaScript语法。 请注意,它以大写字母A开头-是<App /> ,而不是<app /> 。 这是React使用的约定,它使它可以消除常规HTML标签和我们创建的React组件之间的歧义。 如果您不以大写字母开头组件,那么React将无法渲染您的组件。

每当使用JSX时,我们总是必须通过在.js文件中添加以下行来导入React:

import React from 'react';
App.js

现在,我们准备看一下我们的第一个组件。 让我们打开src/app.js

为了创建一个React组件,我们必须创建一个从React.Component继承的类。 这正是class App extends Component线class App extends Component功能。 所有React组件都应实现一个render方法-您可能已经猜到了,所有渲染都在此方法内进行。 render方法必须返回要渲染的标记。

一个小的旁注: className属性等效于HTML中的class属性,用于分配CSS类以进行样式设置。 class是JavaScript中的保留关键字,不能用于属性名称。

让我们回顾一下:

  1. 该组件名为App(大写A)
  2. 它扩展了React.Component
  3. 它必须实现render方法,该方法返回标记。

坏习惯#1-随处可见的类组件

在React中有两种创建组件的方法-类组件和功能组件。 您可能已经注意到,上面的示例使用了一个类组件。 而且,不幸的是,大多数初学者的React教程都鼓励使用Class Components。

类组件有什么问题? 它们很难测试,往往会变得很大,易于分散关注点,将逻辑与表示耦合在一起(这使得调试和测试更加困难)。 通常,您将通过使用Class Components来射击自己。 尤其是如果您是初学者,我建议您完全远离他们。

好的,类组件不好,我明白了。 但是还有哪些选择? 功能组件。 如果组件仅具有render方法,那么它是重构为功能组件的理想选择。 让我们看看如何改善由create-react-app创建的App组件:

看看我们在这里做了什么? 我们删除了该类,并用function App() {...}替换了render方法。 而且,如果我们使用ES6箭头功能,它将看起来更好:

我们已经将类组件转换为一个函数,该函数返回要呈现的标记。

仔细考虑一下……一个返回标记的函数,没有不必要的样板代码,只有纯标记! 不漂亮吗

该功能组件的读取效果更好,信噪比更高。

在本文中,我们将坚持使用类组件,因为它们涉及的抽象较少,并且更易于演示核心React概念。 一旦您熟悉了React基础知识,我强烈建议您阅读我更深入的文章— 通过Recompose掌握React功能组件

道具介绍

道具是React的核心概念。 到底是什么道具? 考虑一下传递给函数的参数。 道具就是这样-将参数传递给组件。

在这里,我们创建了一个Greetings组件,并使用它从App组件中向John Smith致意。 这将导致以下标记:

<div>
<div>Hey you! John Smith!</div>
</div>

{props.name}的大括号表示JavaScript代码。 Greetings组件传递了firstNamelastName作为参数,我们只需通过访问props对象来检索它们。

请注意,组件传递了一个props对象,而不是firstNamelastName两个值。

我们可以通过使用ES6对象解构语法来进一步简化代码:

请注意, (props)被替换为({ firstName, lastName }) 。 这意味着我们只对props对象的这两个属性感兴趣。 这样一来,我们就可以直接访问firstNamelastName值,而不必显式指定props.firstName

如果我们一直使用类组件怎么办?

我不了解您,但是对我来说,这看起来更肿胀了! 我们总是必须显式使用this.props

单一责任原则

亚历山大·安德鲁斯Alexander Andrews)Unsplash发表的 “黑色相机,银色圆形模拟手表,黑色Swiss Gear小刀和黑色手电筒”

单一责任原则是要遵循的最重要的编程原则。 它指出模块应该做一件事,并且应该做好。 单单不遵循这一原则,就可能将任何代码库变成无法维护的噩梦。

我们如何违反这一原则? 最常见的方法是将不相关的内容放置在同一文件中。

在本教程中,我将多次参考“单一责任原则”。

初学者通常将多个组件放在同一文件中。 在这里,我们将Greetings和App组件放置在同一文件中。 这是一个坏习惯,因为这违反了“单一责任原则”。

即使最小的组件(如上面的Greetings组件)也应放在单独的文件中。

让我们将Greetings组件放入其自己的文件中:

然后在App组件中使用它:

import Greetings from "./Greetings";
const App = () => (
...
);

确保文件名与组件名称匹配。 App组件应放置在App.jsGreetings组件应放置在Greetings.js ,依此类推。

介绍状态

状态是React的另一个核心概念。 这是您想要保留数据的地方-可能会发生变化。 是否存储键入到表单元素中的值? 使用状态。 在游戏中跟踪得分? 使用状态。

让我们构建一个简单的表单,以用户的名字作为输入。 请注意,我故意使用类组件来演示该概念。 在我的其他文章《 用Recompose掌握React功能组件》中,我演示了将类组件重构为功能组件的方法

好的,用户可以在表格中输入他的电子邮件,这太好了! 如果您一直在注意,那么您会发现,无论如何,问候语中都会使用约翰这个名字。 如果不是我们所有的用户名都是John,该怎么办? 我们将自己置于非常不舒服的境地。

我们如何使用输入到输入中的值? 在React中,我们不应该直接查询DOM。 这是输入处理程序和状态进入的地方。

状态基本上是一个普通JavaScript对象,它作为属性存储在SimpleForm类组件中。 在这里,我们为类添加值firstName

现在,我们的firstName输入具有onChange事件处理程序。 每次用户在输入中键入键时都会触发。 我们类中的属性this.onFirstNameChange处理onChange事件。

让我们看一下onFirstNameChange属性:

this.setState(...)

这就是我们更新组件状态的方式。 我们不应该直接通过setState方法直接更新组件状态。 为了更新firstName状态值的值,我们只需将具有更新后值的对象传递给setState方法:

{ firstName: event.target.value }

在这种情况下, event.target.value是在表单输入中键入的值。 在这种情况下,这就是用户名。

附带说明:我们尚未将onFirstNameChange定义为方法。 将其定义为类的箭头函数属性,而不是方法,这一点非常重要。 如果我们它定义为一个方法来代替,那么this将被绑定到表单输入调用该方法,而不是类如我们所预期的。 这个小细节经常使初学者绊倒。 这是避免在JavaScript中使用类的另一个原因。

表格验证

布伦南·伯林Brennan Burling)Unsplash发表的 “银色iMac已开启”

现在,让我们使用正则表达式实现简单的表单验证-确保名字至少三个字符,并且仅包含字母。

我们将为onBlur事件添加另一个事件处理程序-每当用户离开输入时它将触发。 我们还将在状态中添加另一个属性firstNameError 。 然后,我们将在输入下方显示验证错误(如果存在错误)。

首先,我们向状态添加了firstNameError属性:

state = {
...
firstNameError: "",
};
验证功能

验证本身在上面的validateName箭头函数中进行。 它只是针对正则表达式测试输入名称:

validateName = name => {
const regex = /[A-Za-z]{3,}/;
return !regex.test(name)
? "The name must contain at least three letters..."
: "";
}

如果验证失败,我们将返回验证错误。 如果验证成功,那么我们将返回一个空字符串(表示没有错误)。 我们在这里使用JavaScript三元表达式来使代码更简洁。

onBlur事件处理程序

让我们看一下onBlur事件处理程序(在用户离开输入时触发):

onFirstNameBlur = () => {
const { firstName } = this.state;
  const firstNameError = this.validateName( firstName );
  return this.setState({ firstNameError });
};

在这里,我们通过使用ES6对象解构语法从状态中提取firstName 。 第一行等效于:

const firstName = this.state.firstName;

然后,我们使用firstName运行上面定义的验证函数,然后使用返回的错误设置firstNameError状态属性。 如果验证失败,则将设置firstNameError 。 如果成功,则将其设置为空字符串。

render方法

现在让我们看一下render()方法:

render() {
const { firstNameError, firstName} = this.state;
...
}

在这里,我们再次使用ES6对象解构从状态中提取值。

<input
...
onBlur={this.onFirstNameBlur}
/>

此行将onFirstNameBlur函数分配为onBlur事件的事件处理程序。

{firstNameError && <div>{firstNameError}</div>}

在这里,我们使用JavaScript的短路评估功能。 仅当值本身为true时,才会呈现包含firstNameError的div。

造型

Maxime Lebrun在《 Unsplash 》上的“低角度照片上的空螺旋楼梯”

如果您一直在关注,那么您可能已经注意到我们的表格不是特别漂亮……让我们通过添加一些内联样式来更改它!

只需在style属性中传递样式即可添加React中的style

我承认我不是设计师,但我的程序员艺术现在看起来好多了。 这是带有验证错误的表格:

坏习惯#3-组件中的样式

在这里,我们遇到了另一种不好的做法,不幸的是这种做法太普遍了—将样式放置在组件的render方法中。 为什么这样不好? 这违反了单一责任原则。 它也使我们的组件杂乱无章,极大地损害了可读性。

有哪些补救办法? 创建一个包含所有样式的特殊style对象。 将styles放在单独的文件中被认为是一个好习惯:

然后在我们的SimpleForm组件中使用它:

这看起来更干净!

总结:将样式放在单独的文件中。

添加更多表单字段

让我们通过添加一个字段来输入姓氏来使表单更有趣:

这里没有太大变化-我们只复制了firstName输入的代码,也复制了其事件处理程序。

我只是说重复吗? 重复代码在软件开发中是一个很大的难题,应不惜一切代价避免重复代码。

坏习惯4:不要拆分组件。

这种不好的做法再次回到违反单一责任原则的状态。 好的书面代码应该读起来就像一首诗,我敢打赌我们组件的render方法读起来不像一首诗。 让我们改变一下。

输入几乎是相同的,并且都需要某种验证。 让我们对组件应用一些重构,然后创建一个可重用的TextField组件:

我只是从render方法中提取了一个组件,将其转换为功能组件,然后将其更改为props:

很好,现在读起来好多了! 我们甚至可以更进一步,并为名字和姓氏创建专用的TextField组件:

在这里,我们只是返回一个预制组件以显示名字。 ({...rest})是新的Object Rest语法-这意味着作为prop传递的所有内容都将保存到rest对象中。 然后将道具传递到TextField我们使用了Object Spread语法{...rest} 。 这将获取rest对象,传播其属性,然后将其向下传递到TextField组件。

换句话说:我们接受传递给FirstNameField任何内容,并将其TextField传递给TextField

同样, LastNameField

这是我们的表格现在的样子:

好多了!

为什么类组件仍然如此糟糕?

  1. 类组件很难测试(与功能组件不同)。
  2. 关注点分离差:如果我们很懒惰,那么我们将把所有东西都放到一个类中,随着时间的流逝,它可能会变成1000行怪兽(我已经看到过多次发生)。
  3. 类组件倾向于将逻辑和表示形式放在一个单独的类中。 这对于分离关注点再次不利。
  4. 类组件不是纯组件,很难推理。 另一方面,功能组件是纯组件-它们将始终导致为相同的输入props渲染相同的标记。
  5. 功能组件强制执行良好的设计。 您不得不考虑设计。
  6. 无需使用this关键字,它一直是造成混淆的主要原因。

源代码

随附的源代码可以在GitHub上找到

下一步是什么?

本教程的时间比我预期的要长。 还有很多要讨论的内容,尤其是与代码组织有关的内容以及其他一些最佳实践。 让我知道您是否有兴趣听取更多评论。

如果您真的想精通React,我强烈建议您阅读另一篇文章: 使用Recompose精通React Functional Components

请确保关注我以获取更多有关React和JavaScript的文章!

翻译自: https://hackernoon.com/react-js-a-better-introduction-to-the-most-powerful-ui-library-ever-created-ecd96e8f4621

react.js复用ui

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值