当前位置:   article > 正文

FastAPI+React全栈开发18 JSX和组件

FastAPI+React全栈开发18 JSX和组件

Chapter04 Setting Up a React Workflow

18 JSX and the Components

FastAPI+React全栈开发18 JSX和组件

We might safely say that JSX is the glue that holds the whole React concept together. The smallest building blocks of a React page or app are so-called React elements. A simple element might be as follows.

我们可以肯定地说,JSX是将整个React概念结合在一起的粘合剂。React页面或应用程序中最小的构建块是所谓的React元素。一个简单元素可能如下所示。

const title = <h1>The Car Sales App</h1>
  • 1

This is an interesting concept, it looks like an H1 HTML element, but it also definitely looks like JavaScript, too. And you would be right, JSX enables us to create React elements that can be inserted into React’s virtual DOM tree that is different from the actual HTML. React takes care of the tedious job of updating the DOM to match the virtual DOM and compiles the JSX elements (through something called Babel) into actual HTML elements. Why JSX, you wonder? Well, first, it is a ful-fledged programming language, JavaScript in all its glory and power. React elements are immutable, once we create them, we cannot change them, and as the React site states, they are like single frames in a movie. However, they can be replaced with new elements.

这是一个有趣的概念,它看起来像一个H1 HTML元素,但它也肯定看起来像JavaScript。你是对的,JSX使我们能够创建React元素,这些元素可以插入到React的虚拟DOM树中,这与实际的HTML不同。React负责更新DOM以匹配虚拟DOM的繁琐工作,并将JSX元素(通过Babel)编译为实际的HTML元素。您想知道为什么是JSX ?首先,它是一种成熟的编程语言,JavaScript拥有其所有的荣耀和功能。React元素是不可变的,一旦我们创建了它们,我们就不能改变它们,正如React网站所说,它们就像电影中的单个帧。然而,它们可以被新的元素所取代。

It is important to note that every React component, including our App file, which is currently the only component that we have, must return one and only one element, a div or fragment (essentially, an empty tag, <>) and all the React elements encolosed in it. Let’s try and write some simple elements and modify our App.js file to look like this.

重要的是要注意,每个React组件,包括我们的App文件,它是目前我们拥有的唯一组件,必须返回一个且只有一个元素,一个div或fragment(本质上是一个空标签,<>)和包含在其中的所有React元素。让我们尝试编写一些简单的元素,并将App.js文件修改为如下所示。

function App() {
    let data = [
        "Fiat",
        "Peugeot",
        "Ford",
        "Renault",
        "Citroen",
    ]
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <h1 className="bg-slate-500 text-white text-center">
                This is a Tailwind styled site!
            </h1>
            <div>
                {data.map(v => {
                    return (
                        <div>
                            Cars listed as
                            <span className="font-bold">{v.toUpperCase()}</span>
                        </div>
                    )
                })}
            </div>
        </div>
    );
}

export default App;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Let’s take a look at what we just did, or better yet, what JSX was able to provide us. First, we have declared some data, a simple list of car brands in an array. For now, we will pretend that we got thisdata from an external API. After all, it’s all JavaScript. Then, in the return statement, we were able to map over this array using the JavaScript map function, we called the elements of the el array. Finally, we return these elements, in this case, they are strings, and we wrap them in template literals (another ES6 feature) and, just to be fancy, transform them to uppercase. The whole function returns exactly one div element. Since class is a reserved name in JavaScript, React uses the className keyword, and we can see that we used it quite a bit since Tailwind is very verbose.

让我们看看我们刚刚做了什么,或者更好的是,看看JSX能够提供什么。首先,我们声明了一些数据,一个数组中的简单汽车品牌列表。现在,我们将假装从外部API获取这些数据。毕竟,都是JavaScript。然后,在返回语句中,我们可以使用JavaScript map函数对这个数组进行映射,我们调用el数组的元素。最后,我们返回这些元素,在本例中,它们是字符串,我们将它们包装在模板字面量中(ES6的另一个特性),并且为了美观起见,将它们转换为大写。整个函数只返回一个div元素。由于class在JavaScript中是一个保留名称,React使用className关键字,我们可以看到我们使用了它很多次,因为Tailwind非常啰嗦。

Finally, let’s add a little something to our App.js file, so React doesn’t complain in the console. Change the return statement by adding a key property.

最后,让我们在App.js文件中添加一些东西,这样React就不会在控制台中抱怨了。通过添加一个键属性来更改返回语句。

<div>
    {data.map(v => {
        return (
            <div key={v}>
                Cars listed as
                <span className="font-bold">{v.toUpperCase()}</span>
            </div>
        )
    })}
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

The key is a unique identifier that React needs anytime it creates arrays of DOM elements, so it knows which one to replace, keep, or remove. This is a rather simplistic example, but it shows the basics of he power of React’s handwriting JSX. The important thing to remember is that we have to return exactly one element, be it a div element, a title, or a React fragment.

键是React在创建DOM元素数组时需要的唯一标识符,因此它知道要替换、保留或删除哪个元素。这是一个相当简单的例子,但它展示了React手写JSX的基本功能。重要的是要记住,我们必须返回一个元素,可以是div元素、标题,也可以是React片段。

To sum it up, and for those of you who might be coming from a different UI framework or library (such as Vue.js, Svelte, or Angular), React does not have a proper templating language with a dedicated syntax for looping over arrays of objects or if-else constructs. Instead, you can rely on the full power of JavaScript and use the standard language features such as map for iterating through arrays, filter for filtering data, ternary operators for if-else constructs, template literals for string interpolations, and more. Coming from classical templating languages such as Jinja2 and Handlebars, I must admit it can take some time to adjust. But after a while, it becomes very natural, and you don’t really have to thnk much about rules and what can and cannot be done (almost anything can!).

总而言之,对于那些可能来自不同UI框架或库(如Vue.js, Svelte或Angular)的人来说,React没有适当的模板语言,没有专门的语法来循环对象数组或if-else结构。相反,您可以充分利用JavaScript的强大功能,并使用标准语言特性,如map用于遍历数组,filter用于过滤数据,三元操作符用于if-else结构,模板文字用于字符串插值,等等。来自经典的模板语言,如Jinja2和Handlebars,我必须承认它需要一些时间来调整。但过了一段时间,它就会变得很自然,你真的不需要考虑太多规则和什么可以做什么不可以做(几乎任何事情都可以!)

Now we will speak about arguably React’s most important feature - components.

现在我们来谈谈React最重要的特性 - 组件。

Components

The whole idea or paradigm of moder web development (we’re talking 2020s modern) is built around the concept of breaking complex UIs into smaller, more manageable units components. In React since its beginning, components could be created by extending a JavaScript component class, using the render method, and defining functions, With the introduction of hooks, which are special functions that allow us to interact with the state and life cyclle of the components directly, React development became much more flexible and concise, at least in my humble opinion. But let’s talk about components.

现代web开发的整个思想或范式(我们谈论的是20世纪20年代的现代)是围绕将复杂的ui分解成更小、更易于管理的单元组件的概念构建的。在React中,从一开始,组件就可以通过扩展JavaScript组件类、使用render方法和定义函数来创建。随着hooks的引入,这些特殊的函数允许我们直接与组件的状态和生命周期进行交互,React开发变得更加灵活和简洁,至少在我看来是这样。我们来讨论一下组件。

Components are reusable pieces of the UI, and we can think of them as functions returning JSX, pieces or units of UI. One of the first stages of planning the development of a react site is the identification of areas, that is, pieces that could be abstracted into components and reused in some way or, at the very least, abstracted into separate units.

组件是UI的可重用部分,我们可以将它们视为返回JSX、UI的部分或单元的函数。规划反应站点开发的第一个阶段之一是识别区域,即可以抽象为组件并以某种方式重用的部分,或者至少可以抽象为单独的单元。

Let’s try to create a minimal component for displaying the header on a page. The component should have an easy task: just to display the header, in our case, the title of the page, and maybe some simple navigation links.

让我们尝试创建一个用于在页面上显示标题的最小组件。组件应该有一个简单的任务:只显示标题,在我们的例子中是页面的标题,也许还有一些简单的导航链接。

Functional components in React.js are defined as files with .jsx or .js extensions, and like our App.js file (which is also a component, the root component), they must return a single JSX element. The filenames should be capitalized. This is a great moment in which to use our previously installed React extension for Visual Studio Code as it provides useful snippets for creating standard components. Follow these steps.

React.js中的功能组件被定义为扩展名为. JSX或.js的文件,就像我们的App.js文件一样(它也是一个组件,根组件),它们必须返回一个JSX元素。文件名应该大写。现在是使用我们之前为Visual Studio Code安装的React扩展的好时机,因为它为创建标准组件提供了有用的代码片段。遵循以下步骤。

Let’s create a folder called components in our src folder along with a new file called Header.js in it.

让我们在src文件夹中创建一个名为components的文件夹,并在其中创建一个名为Header.js的新文件。

Now, open the newly created file and type in rafce. The deditor should suggest something cryptically to you, called reactArrowFunctionExportComponent.

现在,打开新创建的文件并输入rface。奉献者应该向你隐晦地建议一些东西,叫做reactArrowFunctionExportComponent。

Select it and you will se your file filled with typical ES6 arrow function component exported.

选择它,您将看到您的文件被导出的典型ES6箭头功能组件填充。

const Header = () => {
    return (
        <div>Header</div>
    )
}

export default Header
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

This file defines a single JSX topmost element, called Header, and exports it at the bottom.

该文件定义了一个JSX最顶层的元素Header,并在底部导出它。

Let’s make some edits tothis file, making use of our Tailwind CSS framework classes: We will make a div element on the left-hand side containing the title and a couple of links on the right-hand side. At this point, we will not worry about responsiveness or fancy coloring. I just want to create some contrast so that we can see what we have made.

让我们利用顺风CSS框架类对该文件进行一些编辑:我们将在左侧创建一个div元素,其中包含标题,并在右侧创建两个链接。在这一点上,我们不会担心响应性或花哨的颜色。我只是想创造一些对比,这样我们就能看到我们做了什么。

const Header = () => {
    return (
        <div className="flex flex-row bg-orange-600 text-white align-middle justify-center p-5">
            <h1>Cars Sales App</h1>
        </div>
    )
}

export default Header
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

After these edits, which we will explain in a bit and which are purely Tailwind-related, go ahead and import the first component to our App.js file. Imports are handled in terms of the relative path, just remember that the dot denotes the current directory of the file (src in our case), while /components is the folder in which we are keeping our components.

在完成这些编辑(我们将稍后解释)之后,继续将第一个组件导入到App.js文件中。导入是根据相对路径来处理的,只要记住点表示文件的当前目录(在我们的例子中是src),而/components是我们保存组件的文件夹。

The App.js file should now look like this.

App.js文件现在看起来应该是这样的。

import Header from "./components/Header";

function App() {
    let data = [
        {id: 1, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 2, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 3, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
        {id: 4, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 5, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 6, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
    ]
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <div>
                {data.map(v => {
                    return (
                        <div key={v.id}>
                            Cars listed as
                            <span className="font-bold">{v.brand.toUpperCase()}</span>
                        </div>
                    )
                })}
            </div>
        </div>
    );
}

export default App;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

If you reload our app, I’m just kidding, React should be doing that job for you if you haven’t stopped the npm run start process. You will see that our simple web page now has a simple header component. It is an h1 element and has some basic formatting, it is orange and centered. We imported the component as a self-closing tag.

如果你重新加载我们的应用,我只是开玩笑,如果你没有停止npm run start进程,React应该会替你完成这项工作。您将看到我们的简单网页现在有一个简单的标题组件。它是一个h1元素,有一些基本的格式,它是橙色的,居中。我们将组件导入为自关闭标签。

So, you just made your first, very simple, React functional component. In this way, we can break down the functionality of our entire website: We can add a footer, maybe some navigation, and more. In fact, the process of breaking an app down into components and deciding what should consititue a single component is so important that the React documentation has an excellent page dedicated to the process: https://reactjs.org/docs/thinking-in-react.html.

你刚刚制作了第一个非常简单的React功能组件。通过这种方式,我们可以分解整个网站的功能:我们可以添加页脚,或者一些导航,等等。事实上,将应用程序分解成组件并决定应该由什么组成单个组件的过程是如此重要,以至于React文档中有一个很棒的页面专门用于此过程:https://reactjs.org/docs/thinking-in-react.html。

Crafting components like this is nice and quick, but it can become boring if the output is fixed, so to speak. Fortunately, React components are functions, and functions can take arguments, and usually, they are able to do something useful with those arguments. Let’s say that we want to create a component that will replace our rather ugly-looking list of car brands and display the information in a more eye-pleasing and informative way. We can then pass the data for each car in our data array (an object) and have it formatted the way we want.

制作这样的组件很好也很快速,但如果输出是固定的,它就会变得很无聊。幸运的是,React组件是函数,函数可以接受参数,通常,它们能够使用这些参数做一些有用的事情。假设我们想要创建一个组件,它将取代我们看起来相当丑陋的汽车品牌列表,并以一种更令人赏心悦目和信息丰富的方式显示信息。然后,我们可以在数据数组(一个对象)中传递每辆车的数据,并按照我们想要的方式对其进行格式化。

Let’s redo our procedure for building components. Follow these steps.

让我们重做构建组件的过程。遵循以下步骤。

Create a new file in components folder, name it Card.js, and type rafce in order to get the VSC extention to fire. You will then see something like this.

在components文件夹中创建一个新文件,将其命名为Card.js,并输入rafce以启动VSC扩展。然后你会看到这样的东西。

const Card = () => {
    return (
        <div>Card</div>
    )
}
export default Card
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Now, let us import the Card component into our App.js file in the same way as we did with the Header.

现在,让我们将Card组件导入到App.js文件中,就像我们导入Header组件一样。

import Header from "./components/Header";
import Card from "./components/Car";

function App() {
    let data = [
        {id: 1, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 2, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 3, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
        {id: 4, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 5, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 6, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
    ]
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <div>
                {data.map(v => {
                    return (
                        <Card key={v.id} car={v}/>
                    )
                })}
            </div>
        </div>
    );
}

export default App
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

Now, instead of returning the divs when mapping through our data, we are returning our Card component and passing it the key (that is, the ID of he car object, note that it just has to be unique or React will yell at us!). Additionally, we are passing it something that we called card and set to the element, the car object.

现在,当映射我们的数据时,我们不是返回div,而是返回Card组件并传递key(即汽车对象的ID,注意它必须是唯一的,否则React会向我们大喊大叫!)另外,我们给它传递了card,并设置为元素car对象。

If you take a look at our page, you will not be impressed, it’s just a bunch of Tailwind-bland Card texts. However, we did achive something, we passed data through props (short for properties) to each Card. We just have to “accept” it in the component.

如果你看看我们的页面,你不会留下深刻的印象,它只是一堆Tailwind式的卡片文本。然而,我们确实实现了一些东西,我们通过props(属性的简称)将数据传递给每个Card。我们只需要在组件中“接受”它。

Let’s call this function argument props and log it to the console. In the Card.js file, modify the first two lines.

让我们调用这个函数参数props并将其记录到控制台。在Card.js文件中,修改前两行。

const Card = (props) => {
    console.log(props)
    return (
        <div>Card</div>
    )
}
export default Card
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

The app will not change, but if you take a look at the console output in Google Chrome, you will see that we are getting back all of the car objects inside our Card components.

应用程序不会改变,但是如果你看一下Google Chrome中的控制台输出,你会看到我们正在返回Card组件中的所有汽车对象。

Now we can go on and populate the Card with the data.

现在我们可以继续使用数据填充Card。

const Card = (props) => {
    return (
        <div>
            {props.car.brand}
            {props.car.model}
            {props.car.year}
            {props.car.price}
            {props.car.color}
        </div>
    )
}
export default Card
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

We’ve got something that will not win any design awards but will get all of our data back into the component.

我们有一些东西,不会赢得任何设计奖,但会得到我们所有的数据回到组件。

Now you can get creative: dive into some Tailwind CSS documentation and come up with a card style that you like. I am going to make something really simple, and I am going to get rid of this props.car repetition by using some JavaScript ES6 destructuring.

现在您可以发挥创意了:深入到一些Tailwind CSS文档中,想出一个您喜欢的卡片样式。我要做一个非常简单的东西,我要去掉这些道具。通过使用一些JavaScript ES6解构来减少重复。

const Card = (props) => {
    let {brand, price, model, year, color} = props.car
    return (
        <div className="shadow-md p-5 flex flex-col">
            <div className="font-extrabold text-center border-b-2">{brand} {model}</div>
            <div>Year: {year}</div>
            <div>
                Price:
                <span className="font-semibold text-orange-600">{price}</span>
            </div>
            <div>Color: {color}</div>
        </div>
    )
}
export default Card
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

In the App.js file, I have just added a couple of classes to the wrapper div: grid gird-cols-3 my-3 gap-3. Once you get the hang of Tailwind CSS, you will be able to read them very easily. We make the div to a grid with three columns, and we add some y-padding and a grid-gap.

在App.js文件中,我刚刚向包装器div添加了两个类:grid grid-cols-3 my-3 gap-3。一旦你掌握了Tailwind CSS的窍门,你就能很容易地阅读它们。我们将div创建为具有三列的网格,并添加一些y填充和网格间隙。

import Header from "./components/Header";
import Card from "./components/Card";

function App() {
    let data = [
        {id: 1, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 2, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 3, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
        {id: 4, brand: "Fiat", color: "green", model: "500L", price: 7000, year: 2020,},
        {id: 5, brand: "Peugeot", color: "red", model: "5008", price: 8000, year: 2018,},
        {id: 6, brand: "Volkswagen", color: "white", model: "Golf 7", price: 8500, year: 2019,},
    ]
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <div className="grid grid-cols-3 my-3 gap-3">
                {data.map(v => {
                    return (
                        <Card key={v.id} car={v}/>
                    )
                })}
            </div>
        </div>
    );
}

export default App;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

We have seen how easy it is to pass props to components, and once we can do that, the sky is the limit! Well, not quite. Props provide one-way communication, but in the majority of apps, you will have to deal with the state.

我们已经看到了将props传递给组件是多么容易,一旦我们能够做到这一点,一切都将是无限的!嗯,不完全是。props提供单向通信,但在大多数应用程序中,你将不得不处理状态。

I will not delve into some of the very technical definitions of state. Since React’s job is to keep the UI in sync with the current situation of our app, we can stick to a more descriptive definition.

我不会深入研究一些非常技术性的状态定义。由于React的工作是保持UI与应用程序的当前状态同步,我们可以坚持使用更具描述性的定义。

A state can be thought of as a set of data that represents the user interface (UI) at any given moment. In our case, the state could be a set of selected cars, which we would like to inquire about and save them for later by giving them stars or some follow icon. Forms can have different states depending on the types of inputs: text fields can be empty (waiting to be filled) or populated, checkboxes can be checked, or unchecked, and drop-down menus can be selected or not. You get the idea. The state is such an important topic in React (and, to be honest, in other UI frameworks and libraries) that entire books and conferences are dedicated to it. In this chapter, we will barly scratch the surface of how React Hooks help us to define and maintain state throughout our component’s life cycle, that is, while the component is alive.

可以将状态视为在任何给定时刻表示用户界面(UI)的一组数据。在我们的例子中,状态可以是一组选定的汽车,我们想要查询它们,并通过给它们星星或一些跟随图标来保存它们。根据输入的类型,表单可以具有不同的状态:文本字段可以为空(等待填充)或填充,复选框可以选中或不选中,下拉菜单可以选中或不选中。你懂的。状态在React中是一个非常重要的话题(说实话,在其他UI框架和库中也是如此),以至于整个书籍和会议都致力于此。在本章中,我们将仅仅触及React Hooks如何帮助我们在组件的整个生命周期中定义和维护状态的表面,也就是说,当组件还活着的时候。

In the early days of yore, you had to create React components by extending the JavaScript classes and maintian the state through a series of this calls that made working with the state a bit verbose and cumbersome. With the introduction of React Hooks, we have at our disposal sveral easy mechanisms for dealing with the state, from very simple ones to more complex ones. In the text section, we are going to take a look at a couple of React Hooks and learn how they can help us to write concise and maintainable code.

在早期,您必须通过扩展JavaScript类来创建React组件,并通过一系列this调用来维护状态,这使得处理状态有点冗长和麻烦。随着React Hooks的引入,我们可以使用几种简单的机制来处理状态,从非常简单的到更复杂的都有。在文本部分,我们将看看几个React Hooks,并学习它们如何帮助我们编写简洁且可维护的代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/383420
推荐阅读
相关标签
  

闽ICP备14008679号