当前位置:   article > 正文

react 对象克隆_使用React和ElasticSearch构建一个Airbnb克隆

elasticsearch react

react 对象克隆

Alright, alright, alright. I heard you like React, and Airbnb, but what is this Elasticsearch thing I'm talking about. Don't worry. This blog post will give you a kickstart in understanding the basics of Elasticsearch - What is it? Why should you care about it? and, how can you use it with React to build powerful apps painlessly.

好吧好吧好吧 我听说您喜欢React和Airbnb,但我所说的Elasticsearch是什么。 不用担心 这篇博客文章将为您提供一个了解Elasticsearch基础的起点-这是什么? 你为什么要关心它? 以及如何与React一起使用它来轻松构建强大的应用程序。

What is Elasticsearch and why should you care about it?

什么是Elasticsearch?为什么要关心它?

Elasticsearch is a super fast open-source full-text search engine. It allows you to store, search, and analyze big volumes of data quickly (we are talking milliseconds here). It is generally used as the underlying engine/technology that powers applications that have complex search features and requirements. You can read more about it here.

Elasticsearch是一个超快速的开源全文搜索引擎 。 它使您可以快速存储搜索分析大量数据(此处我们以毫秒为单位)。 它通常用作支持具有复杂搜索功能和要求的应用程序的基础引擎/技术。 您可以在此处了解更多信息。

With Elasticsearch, you can build build a fast search utilizing its powerful query DSL. However, setting up elasticsearch correctly requires a lot of work. For instance, the data mapping, analyzers and tokenizers need to be set correctly or you may not receive accurate search results back. Besides, the more filters that get applied along with the search query, the more complex the resulting search query becomes.

借助Elasticsearch,您可以利用其强大的查询DSL构建快速搜索。 但是,正确设置弹性搜索需要大量工作。 例如,数据映射,分析器和标记器必须正确设置,否则您可能无法收到准确的搜索结果。 此外,与搜索查询一起应用的过滤器越多,结果搜索查询将变得越复杂。

We, at Appbase, have built some open-source tools to help you do all these things with the matter of some clicks.

我们在Appbase上建立了一些开源工具,可以帮助您完成一些点击操作。

  • Tool to add data into Elasticsearch - Importer

    将数据添加到Elasticsearch的工具- 导入器
  • Tool to view Elasticsearch data like an excel sheet - Data Browser

    像Excel工作表一样查看Elasticsearch数据的工具- 数据浏览器
  • Tool to generate relevant Elasticsearch queries easily - Query Builder

    轻松生成相关Elasticsearch查询的工具- 查询生成器

In this blog post, with the help of some of these tooling, we will utilize the strengths of Elasticsearch with React to build powerful apps.

在此博客文章中,在其中一些工具的帮助下,我们将利用Elasticsearch和React的优势来构建功能强大的应用程序。

How to use Elasticsearch with React?

如何在React中使用Elasticsearch?

We will be using - ReactiveSearch, an open-source React UI components library for Elasticsearch that I am a contributor to. It offers a range of highly customizable rich UI components that can connect with any Elasticsearch server and provide you an appropriate default search query for all generic use-cases (like Ecommerce, Yelp, Meetups, etc) bundled into these components.

我们将使用- ReactiveSearch ,一个开源阵营的UI组件库Elasticsearch,我是一个贡献者。 它提供了一系列高度可定制的丰富UI组件,它们可以与任何Elasticsearch服务器连接,并为您绑定到这些组件中的所有通用用例(例如,电子商务,Yelp,Meetups等)提供适当的默认搜索查询。

Wait, why do I need ReactiveSearch now?

等等,为什么我现在需要ReactiveSearch?

ReactiveSearch simplifies the entire process of connecting to an elasticsearch index, making queries, fetching and rendering results in sleek UI, not just that, it also lets you make your components talk to each other, i.e. if Component-A gets updated, Component-B gets to know and it can update itself without needing any manual interaction.

ReactiveSearch简化了连接到Elasticsearch索引,在光滑的UI中进行查询,获取和呈现结果的整个过程,不仅如此,它还使您能够使组件彼此对话,即,如果Component-A得到更新,则Component-B知道了,它可以更新自己,而无需任何手动交互。

This whole component-to-component subscription comes in handy when you have dynamic filters present on your screen, such as in case of E-commerce apps where Selecting a category of Appliances, also changes the sub-categories available, their prices and what not.

当您在屏幕上显示动态过滤器时,例如在“电子商务”应用程序中选择“设备类别”也会更改可用的子类别,其价格以及不更改哪些子类别时,整个组件到组件的订阅将非常有用。 。

ReactiveSearch helps you create significantly smarter apps easily and in a declarative fashion.

ReactiveSearch可帮助您以声明性方式轻松创建智能得多的应用程序。

Even if you’ve never used Elasticsearch before you should be able to follow along with this blog post and build your very own Elasticsearch powered search UI using ReactiveSearch.

即使您从未使用过Elasticsearch,也应该能够继续阅读本博文,并使用ReactiveSearch构建自己的Elasticsearch驱动的搜索UI。

Here is an example that generates a searchbox UI with some suggestions as you type.

这是一个示例,该示例会在您键入时生成带有一些建议的搜索框用户界面。

  1. <DataSearch
  2. componentId="search"
  3. dataField="name"
  4. placeholder="Search housings..."
  5. iconPosition="left"
  6. />

我们需要的东西 ( Things we will need )

In order to build an Airbnb-like search application, we will need a set of things before we get started with writing actual code:

为了构建类似于Airbnb的搜索应用程序,在开始编写实际代码之前,我们需要做一些准备工作:

  • Dataset for Airbnb housing properties - We found an amazing dataset for Airbnb Seattle at https://www.kaggle.com/airbnb/seattle which we will be using for our app in this tutorial.

    Airbnb房屋属性的数据集 -我们在https://www.kaggle.com/airbnb/seattle上找到了一个很棒的Airbnb Seattle数据集,我们将在本教程中将其用于我们的应用程序。

  • Elasticsearch hosting - You can set up and install an Elasticsearch server by following the official installation guide, or you can create a free account at appbase.io which provides Elasticsearch hosting as a service and is easy to use. For simplicity, we will be using appbase.io service to get started with.

    Elasticsearch托管 -您可以按照官方安装指南来设置和安装Elasticsearch服务器,也可以在appbase.io上创建一个免费帐户,该帐户将Elasticsearch托管作为服务提供并且易于使用。 为简单起见,我们将使用appbase.io服务开始使用。

I've already created an appbase app with airbnb data-set. You can check out the cleaned up dataset over here in the data browser tool:

我已经使用airbnb数据集创建了一个appbase应用程序。 您可以在此处使用数据浏览器工具检出清理后的数据集:

Airbnb Dataset Browser

The credentials of the above app are:

上面的应用程序的凭据是:

  1. app="housing"
  2. credentials="0aL1X5Vts:1ee67be1-9195-4f4b-bd4f-a91cd1b5e4b5"
  3. type="listing"

You can clone this dataset and generate credentials for your very own app by clicking on the Clone this app button on the bottom left.

您可以通过单击左下方的“ 克隆此应用程序”按钮来克隆此数据集并为您自己的应用程序生成凭据。

建立 ( Setup )

If you have tried out the Create React App before, you will feel right at home as we build this app.

如果您以前尝试过Create React App ,那么在我们构建此应用程序时,您会感到宾至如归。

Create React App

创建React应用

Initialize the CRA setup. We will use yarn as the package manager, you can also use npm instead.

初始化CRA设置。 我们将使用yarn作为包管理器,也可以使用npm代替。

  1. yarn global add create-react-app
  2. create-react-app airbedscd airbeds
  3. yarn start

One of the great benefits of using CRA is that it works without requiring to set up a build configuration.

使用CRA的一大好处是,它无需设置构建配置即可工作。

At this point, you should have a directory structure similar to this:

此时,您应该具有类似于以下的目录结构:

  1. airbeds
  2. ├── README.md
  3. ├── node_modules
  4. ├── package.json
  5. ├── .gitignore
  6. ├── yarn.lock
  7. ├── public
  8. │ └── favicon.ico
  9. │ └── index.html
  10. │ └── manifest.json
  11. └── src
  12. └── App.css
  13. └── App.js
  14. └── App.test.js
  15. └── index.css
  16. └── index.js
  17. └── logo.svg
  18. └── registerServiceWorker.js

Install reactivesearch

安装reactsearch

Next, we will install ReactiveSearch.

接下来,我们将安装ReactiveSearch。

yarn add @appbaseio/reactivesearch@2.16.1

(Note: This post is compatible with ReactiveSearch v2, hence the use of 2.16.1 version. v3 has a very different syntax structure, I will update this post at a later point to work with v3.)

注意:此文章与ReactiveSearch v2兼容,因此使用2.16.1版本。v3具有非常不同的语法结构,我将在以后的某个位置更新此文章以使用v3。)

Next step is to connect with our elasticsearch index at appbase (or your own Elasticsearch server if you are not using appbase).

下一步是在应用程序库(如果您不使用应用程序库,或者您自己的Elasticsearch服务器)上与我们的Elasticsearch索引连接。

Adding the first ReactiveSearch component: ReactiveBase

添加第一个ReactiveSearch组件:ReactiveBase

All the ReactiveSearch components are wrapped inside a container component - ReactiveBase which glues the Elasticsearch index and the ReactiveSearch components together. We’ll use this in /src/App.js:

所有ReactiveSearch组件包装容器组件内- ReactiveBase其中胶合的Elasticsearch指数和ReactiveSearch组分一起。 我们将在/src/App.js使用它:

  1. import React, { Component } from 'react';
  2. import { ReactiveBase } from '@appbaseio/reactivesearch';
  3. class App extends Component {
  4. render() {
  5. return (
  6. <section className="container">
  7. <ReactiveBase
  8. app="housing"
  9. credentials="0aL1X5Vts:1ee67be1-9195-4f4b-bd4f-a91cd1b5e4b5"
  10. type="listing"
  11. >
  12. Hello from ReactiveSearch!
  13. </ReactiveBase>
  14. </section>
  15. );
  16. }
  17. }
  18. export default App;

Notice that we are using the app credentials from the previous step. You may use the ones I’ve provided here as is, or if you cloned this dataset in your own app earlier, you can get them from the app’s credentials page - copy the Read-only API key.

请注意,我们正在使用上一步中的应用程序凭据。 您可以按原样使用我在此处提供的内容,或者如果您以前在自己的应用程序中克隆了此数据集,则可以从应用程序的凭据页面获取它们-复制Read-only API密钥。

If you’re already familiar with Elasticsearch, and have decided to use an Elastisearch server of your own, you will need to pass a url prop referring to your own Elasticsearch cluster URL.

如果您已经熟悉Elasticsearch,并已决定使用自己的Elastisearch服务器,则需要传递一个引用自己的Elasticsearch集群URL的url属性。

Now let’s start the server with yarn start.

现在,让我们用yarn start启动服务器。

添加UI组件 ( Adding UI Components )

Components we will need to begin with:

我们将需要开始的组件:

  • First in the list is a calendar UI which allows a user to select a range of dates for accommodation. The component looks like:

    列表中的第一个是日历UI,它允许用户选择住宿日期的范围。 该组件如下所示:
  1. import { DateRange } from '@appbaseio/reactivesearch';
  2. <DateRange
  3. dataField="date_from"
  4. componentId="DateRangeSensor"
  5. title="When"
  6. numberOfMonths={1}
  7. queryFormat="basic_date" // yyyyMMdd
  8. />

where,

哪里,

componentId is a mandatory prop which requires specifying a unique string which indentifies this particular component. It is used internally by ReactiveSearch lib, as well as by some of the other user facing props.

componentId是强制性道具,需要指定一个唯一字符串来标识此特定组件。 它由ReactiveSearch lib内部以及其他一些面向props的用户内部使用。

dataField prop tells DataSearch which fields to query on. It can take either a string (single field) or an Array of strings (multiple fields).

dataField属性告诉DataSearch要查询哪些字段。 它可以使用一个字符串(单个字段)或一个字符串数组(多个字段)。

Since dates can be written in many different formats, like milliseconds since epoch, or yyyyMMdd, or YYYY-MM-DD or one with an associated timestamp etc, we need to tell the DateRange component about the indexed date field’s data format using the queryFormat prop. You can read more about the DateRange component in its document reference over here.

由于日期可以写在许多不同的格式,如自毫秒为单位,或YYYYMMDD,或YYYY-MM-DD或具有相关联的时间戳等,我们需要使用讲述索引日期字段的数据格式DATERANGE组件queryFormat道具。 您可以在此处的文档参考中了解有关DateRange组件的更多信息。

  • Then comes the guest selection UI which allows a user to filter properties by the number of guests that can be accommodated.

    然后是来宾选择界面,该界面允许用户按可容纳的来宾数量筛选属性。

Here, we will use NumberBox component:

在这里,我们将使用NumberBox组件:

  1. import { NumberBox } from '@appbaseio/reactivesearch';
  2. <NumberBox
  3. componentId="GuestSensor"
  4. dataField="accommodates"
  5. title="Guests"
  6. defaultSelected={2}
  7. data={{
  8. start: 1,
  9. end: 16
  10. }}
  11. />

where,

哪里,

the data prop accepts an object with start and end values for the minimum and maximum guests that will be accommodated and the defaultSelected prop initializes with the component’s UI with a value from within the data range.

data prop接受一个对象,该对象的开始和结束值将包含要容纳的最小和最大guest,并且defaultSelected prop使用组件UI初始化,其值在数据范围内。

  • Next up is the price filter which allows a user to filter accommodations based on their budgets:

    接下来是价格过滤器,它使用户可以根据预算过滤住宿:
  1. import { RangeSlider } from '@appbaseio/reactivesearch';
  2. <RangeSlider
  3. componentId="PriceSensor"
  4. dataField="price"
  5. title="Price Range"
  6. range={{
  7. start: 10,
  8. end: 250
  9. }}
  10. rangeLabels={{
  11. start: "$10",
  12. end: "$250"
  13. }}
  14. defaultSelected={{
  15. start: 10,
  16. end: 50
  17. }}
  18. stepValue={10}
  19. react={{
  20. and: ["DateRangeSensor"]
  21. }}
  22. />

Here, we created a RangeSlider, which has a configurable range prop, that controls the bounds of the slider, and also allow us to add labels and pre-select a range using rangeLabels and defaultSelected props. You can read about the RangeSlider component over here.

在这里,我们创建了一个RangeSlider,它具有可配置的范围道具,它控制滑块的边界,还允许我们添加标签并使用rangeLabelsdefaultSelected道具预先选择一个范围。 您可以在此处阅读有关RangeSlider组件的信息

This time around, we also use a new react prop, which updates the prices dynamically based on the selected date range and number of guests. Every time there is a change in the dates or the number of guests, the price range histogram changes. This is a key feature of ReactiveSearch that lets you create dynamically updating UI components. You can read more about it here.

这次,我们还使用了一个新的react道具,该道具会根据所选的日期范围和客人人数动态更新价格。 每当日期或来宾人数发生变化时,价格范围直方图都会发生变化。 这是ReactiveSearch的一项关键功能,可让您创建动态更新的UI组件。 您可以在此处了解更多信息。

  • Lastly, we will need those fancy Airbnb cards to show the search results:

    最后,我们将需要这些精美的Airbnb卡来显示搜索结果:
  1. import { ResultCard } from '@appbaseio/reactivesearch';
  2. <ResultCard
  3. componentId="SearchResult"
  4. dataField="name"
  5. from={0}
  6. size={10}
  7. onData={this.onData}
  8. pagination={true}
  9. react={{
  10. and: ["GuestSensor", "PriceSensor", "DateRangeSensor"]
  11. }}
  12. />

Besides all the straight-forward props, ResultCard takes in a special onData prop which is a callback function that returns the mappings between the elements of the ResultCard and the fields in the database. For our case, the onData method would look something like this:

除了所有简单明了的道具外,ResultCard还带有一个特殊的onData道具,它是一个回调函数,该函数返回ResultCard的元素与数据库中的字段之间的映射。 对于我们的情况,onData方法将如下所示:

  1. onData(data) {
  2. return {
  3. image: data.image,
  4. title: data.name,
  5. description: (
  6. <div>
  7. <div className="price">${data.price}</div>
  8. <p className="info">{data.room_type} · {data.accommodates} guests</p>
  9. </div>
  10. ),
  11. url: data.listing_url,
  12. };
  13. }

Here, we are returning an object that maps the image, title, desc and url fields of the card layout to the specific fields in the the database or a valid React element expressed in JSX.

在这里,我们将返回一个对象,该对象将卡布局的图像,标题,desc和url字段映射到数据库中的特定字段或以JSX表示的有效React元素。

结合元素 ( Combining the Elements )

Lets put these these components together in the src/App.js file. While doing this, I will also add placeholder classes in the className and innerClass props that we will inject via a stylesheet in the next section.

让我们将这些组件放到src/App.js文件中。 在此过程中,我还将在innerClass通过样式表插入的classNameinnerClass道具中添加占位符类。

  1. import React from 'react';
  2. import { ReactiveBase, NumberBox, DateRange, RangeSlider, ResultCard } from '@appbaseio/reactivesearch';
  3. export default () => (
  4. <div className="container">
  5. <ReactiveBase
  6. app="housing"
  7. credentials="0aL1X5Vts:1ee67be1-9195-4f4b-bd4f-a91cd1b5e4b5"
  8. type="listing"
  9. theme={{
  10. primaryColor: '#FF3A4E',
  11. }}
  12. >
  13. <nav className="nav">
  14. <div className="title">airbeds</div>
  15. </nav>
  16. <div className="left-col">
  17. <DateRange
  18. dataField="date_from"
  19. componentId="DateRangeSensor"
  20. title="When"
  21. numberOfMonths={2}
  22. queryFormat="basic_date"
  23. initialMonth={new Date('04-01-2017')}
  24. />
  25. <NumberBox
  26. componentId="GuestSensor"
  27. dataField="accommodates"
  28. title="Guests"
  29. defaultSelected={2}
  30. labelPosition="right"
  31. data={{
  32. start: 1,
  33. end: 16,
  34. }}
  35. />
  36. <RangeSlider
  37. componentId="PriceSensor"
  38. dataField="price"
  39. title="Price Range"
  40. range={{
  41. start: 10,
  42. end: 250,
  43. }}
  44. rangeLabels={{
  45. start: '$10',
  46. end: '$250',
  47. }}
  48. defaultSelected={{
  49. start: 10,
  50. end: 50,
  51. }}
  52. stepValue={10}
  53. interval={20}
  54. react={{
  55. and: ['DateRangeSensor'],
  56. }}
  57. />
  58. </div>
  59. <ResultCard
  60. className="right-col"
  61. componentId="SearchResult"
  62. dataField="name"
  63. size={12}
  64. onData={data => ({
  65. image: data.image,
  66. title: data.name,
  67. description: (
  68. <div>
  69. <div className="price">${data.price}</div>
  70. <p className="info">{data.room_type} · {data.accommodates} guests</p>
  71. </div>
  72. ),
  73. url: data.listing_url,
  74. })}
  75. pagination
  76. react={{
  77. and: ['GuestSensor', 'PriceSensor', 'DateRangeSensor', 'search'],
  78. }}
  79. innerClass={{
  80. resultStats: 'result-stats',
  81. list: 'list',
  82. listItem: 'list-item',
  83. image: 'image',
  84. }}
  85. />
  86. </ReactiveBase>
  87. </div>
  88. );

You should see a fully functional UI that works. Slide through the prices and you will see the results change in the card layout. The only thing missing at this point is the layout arrangement and the styles. We will add these in the next step.

您应该会看到一个功能齐全的UI。 滑过价格,您会看到结果在卡布局中有所变化。 此时唯一缺少的是布局安排和样式。 我们将在下一步中添加它们。

添加样式和布局 (Adding Styles and Layout)

ReactiveSearch provides us with scoped styled components, while leaving the choice of layout to the user.

ReactiveSearch为我们提供了具有范围的样式化组件,同时让用户可以选择布局。

To keep things simple, we will use the following stylesheet and get things going:

为简单起见,我们将使用以下样式表并开始进行操作:

  1. .container {
  2. display: flex;
  3. padding-top: 52px;
  4. }
  5. .container .nav {
  6. width: 100%;
  7. position: fixed;
  8. top: 0;
  9. left: 0;
  10. z-index: 1;
  11. display: flex;
  12. justify-content: center;
  13. align-items: center;
  14. background-color: #FF3A4E;
  15. color: #fff;
  16. height: 52px;
  17. padding: 0 25px;
  18. }
  19. .container .left-col {
  20. width: 320px;
  21. height: 100%;
  22. padding: 15px 20px;
  23. position: fixed;
  24. left: 0;
  25. right: 0;
  26. border-right: 1px solid #f0f0f0;
  27. }
  28. .container .left-col > div {
  29. margin: 40px 0;
  30. }
  31. @media all and (max-width: 768px) {
  32. .container .left-col {
  33. position: static;
  34. width: 100%;
  35. height: auto;
  36. border-right: 0;
  37. border-bottom: 1px solid #f0f0f0;
  38. }
  39. }
  40. .container .right-col {
  41. width: calc(100% - 320px);
  42. position: relative;
  43. left: 320px;
  44. padding: 25px 30px;
  45. background-color: #fbfbfb;
  46. }
  47. .container .right-col .list {
  48. margin-bottom: 30px;
  49. }
  50. .container .right-col .list-item {
  51. max-width: none;
  52. min-width: 0;
  53. width: calc(30% - 16px);
  54. height: auto;
  55. background-color: transparent;
  56. border: 0;
  57. border-radius: 0;
  58. box-shadow: none;
  59. position: relative;
  60. padding: 0;
  61. }
  62. .container .right-col .list-item h2 {
  63. padding-bottom: 4px;
  64. }
  65. .container .right-col .list-item .image {
  66. background-size: cover;
  67. }
  68. .container .right-col .list-item .price {
  69. width: 70px;
  70. height: 44px;
  71. background-color: #424242;
  72. position: absolute;
  73. top: 160px;
  74. left: 0;
  75. color: #fafafa;
  76. font-size: 18px;
  77. display: flex;
  78. justify-content: center;
  79. align-items: center;
  80. letter-spacing: 0.03rem;
  81. }
  82. .container .right-col .list-item .info {
  83. color: #555;
  84. font-size: 14px;
  85. margin-bottom: 4px;
  86. }
  87. .container .right-col .result-stats {
  88. text-align: right;
  89. color: #666;
  90. font-size: 15px;
  91. }
  92. @media all and (min-width: 1441px) {
  93. .container .right-col .list-item {
  94. width: calc(25% - 16px);
  95. }
  96. }
  97. @media all and (max-width: 1024px) {
  98. .container .right-col .list-item {
  99. width: calc(50% - 16px);
  100. }
  101. }
  102. @media all and (max-width: 768px) {
  103. .container .right-col {
  104. width: 100%;
  105. position: static;
  106. padding: 25px 15px;
  107. }
  108. }
  109. @media all and (max-width: 480px) {
  110. .container .right-col .list-item {
  111. width: calc(100% - 16px);
  112. margin-bottom: 20px;
  113. }
  114. }

Majority of the styling here is about setting the layout. Add the above css styles to App.css and import it in your App.js with a import './App.css' statement along our other imports and we are done!

此处样式的大多数与设置布局有关。 将上述CSS样式添加到App.css并使用import './App.css'语句以及其他导入内容将其导入到您的App.js ,我们完成了!

In case you are missing a step, you can get the code so far by following these:

如果您错过了一步,则可以按照以下步骤获取到目前为止的代码:

  1. git clone git@github.com:appbaseio-apps/airbeds.git
  2. cd airbeds && git checkout basic-app
  3. yarn && yarn start
  4. # open http://localhost:3000

We went from a boilerplate with CRA to creating an Airbnb-like UI, hopefully well within 60 minutes.

我们从使用CRA的样板程序创建了一个类似于Airbnb的UI,希望可以在60分钟内完成。

接下来我们该怎么办? ( What can we do next? )

We have just scratched the surface here of what’s possible to build. Some ideas that you can build upon here are:

我们只是在这里初步探讨了可能的构建方法。 您可以在此处建立的一些想法是:

  • Add a searchbox or more filters (ReactiveSearch offers 20+ components),

    添加搜索框或更多过滤器(ReactiveSearch提供20多个组件),
  • Add sort options to allow different ordering of the results,

    添加排序选项以允许对结果进行不同的排序,
  • Add an OAuth login flow and only allow signed-in users to see this UI view.

    添加OAuth登录流程,仅允许登录用户查看此UI视图。

Tap on the above image to go to ReactiveSearch Docs to explore further.

点击上面的图片,转到ReactiveSearch文档,以进一步探索。

Go star the Reactivesearch Github project so you can find it when you need to build that awesome search.

成为Reactivesearch Github项目的明星,这样您就可以在需要构建出色搜索时找到它。

翻译自: https://scotch.io/tutorials/build-an-airbnb-clone-with-react-and-elasticsearch

react 对象克隆

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

闽ICP备14008679号