Elasticsearch is one of the most popular full-text search engines which allows you to search huge volumes of data quickly, while React is arguably the best library for building user interfaces. During the past few months I’ve been co-authoring an open-source library, ReactiveSearch, which provides React components for Elasticsearch and simplifies the process of building a search User Interface (UI).
Elasticsearch是最受欢迎的全文搜索引擎之一,它使您可以快速搜索大量数据,而React无疑是构建用户界面的最佳库 。 在过去的几个月中,我一直与他人合着一个开源库ReactiveSearch ,该库为Elasticsearch提供React组件,并简化了构建搜索用户界面(UI)的过程。
This is the app which I’ll be building in this story:
这是我将在此故事中构建的应用程序:
Elasticsearch is a NoSQL database which can search through large amounts of data in a short time. It performs a full-text search on the data which is stored in the form of documents (like objects) by examining all the words in every document.
Elasticsearch是一个NoSQL数据库,可以在短时间内搜索大量数据。 它通过检查每个文档中的所有单词,对以文档(如对象)形式存储的数据执行全文搜索 。
Here’s what the Elasticsearch docs say:
这是Elasticsearch文档所说的:
Elasticsearch is a highly scalable open-source full-text search and analytics engine. It allows you to store, search, and analyze big volumes of data quickly and in near real time.
Elasticsearch是一个高度可扩展的开源全文本搜索和分析引擎。 它使您可以快速,近乎实时地存储,搜索和分析大量数据。
Even if you’ve never used Elasticsearch before you should be able to follow along with this story and build your very own Elasticsearch powered search using React and ReactiveSearch. ?
即使您从未使用过Elasticsearch,也应该能够跟随这个故事并使用React和ReactiveSearch构建自己的以Elasticsearch为动力的搜索。 ?
ReactiveSearch is a React UI components library for Elasticsearch. In order to search data in Elasticsearch, you need to write queries. Then you will need to format and render the JSON data in your UI. ReactiveSearch simplifies the entire process since you don’t need to worry about writing these queries. This makes it easier to focus on creating the UI.
ReactiveSearch是用于Elasticsearch的React UI组件库。 为了在Elasticsearch中搜索数据,您需要编写query 。 然后,您将需要在UI中格式化和呈现JSON数据。 由于您无需担心编写这些查询,因此ReactiveSearch简化了整个过程。 这样可以更轻松地专注于创建UI。
Here is an example that generates a search-box UI with category specific suggestions:
这是一个生成带有特定类别建议的搜索框用户界面的示例:
<CategorySearch
componentId="repo"
dataField={["name", "name.raw"]}
categoryField="language.raw"
/>
This would likely have taken us 100+ lines without the library, and knowledge of Elasticsearch Query DSL to construct the query.
这可能需要我们100多行,而没有库,并且没有Elasticsearch Query DSL的知识来构建查询。
In this post, I’ll use different components from the library to build the final UI.
在本文中,我将使用库中的不同组件来构建最终的UI。
You should try out the final app before we deep-dive. Here’s the CodeSandbox link for the same.
在深入研究之前,您应该尝试最终的应用程序 。 这是相同的CodeSandbox链接 。
Before we start building the UI, we’ll need the dataset containing GitHub repositories in Elasticsearch. ReactiveSearch works with any Elasticsearch index and you can easily use it with your own dataset.
在开始构建UI之前,我们需要Elasticsearch中包含GitHub存储库的数据集。 ReactiveSearch可与任何Elasticsearch索引一起使用,您可以轻松地将其与自己的数据集结合使用 。
For brevity, you can use my dataset or clone it for yourself by following this link and clicking on Clone this App button. This will let you make a copy of the dataset as your own app.
为简便起见,您可以使用我的数据集或通过单击此链接并单击“ 克隆此应用程序”按钮自行克隆它 。 这将使您可以将数据集的副本作为自己的应用程序进行复制。
After you enter an app name, the cloning process should start importing the 26K+ repos to your account.
输入应用程序名称后,克隆过程应开始将26K +存储库导入到您的帐户。
All the repos are structured in the following format:
所有存储库均采用以下格式构成:
{
"name": "freeCodeCamp",
"owner": "freeCodeCamp",
"fullname": "freeCodeCamp~freeCodeCamp",
"description": "The https://freeCodeCamp.org open source codebase and curriculum. Learn to code and help nonprofits.",
"avatar": "https://avatars0.githubusercontent.com/u/9892522?v=4",
"url": "https://github.com/freeCodeCamp/freeCodeCamp",
"pushed": "2017-12-24T05:44:03Z",
"created": "2014-12-24T17:49:19Z",
"size": 31474,
"stars": 291526,
"forks": 13211,
"topics": [
"careers",
"certification",
"community",
"curriculum",
"d3",
"education",
"javascript",
"learn-to-code",
"math",
"nodejs",
"nonprofits",
"programming",
"react",
"teachers"
],
"language": "JavaScript",
"watchers": 8462
}
We will use create-react-app to set up the project. You can install create-react-app by running the following command in your terminal:
我们将使用create-react-app设置项目。 您可以通过在终端中运行以下命令来安装create-react-app:
npm install -g create-react-app
create-react-app gitxplore
cd gitxplore
npm install @appbaseio/reactivesearch
You may also add fontawesome CDN, which we’ll be using for some icons, by inserting the following lines in /public/index.html
before the </body>
tag ends:
您还可以添加fontawesome CDN(我们将在其中使用某些图标),方法是在</body>
标记结束之前在/public/index.html
插入以下行:
<script defer src="https://use.fontawesome.com/releases/v5.0.2/js/all.js"></script>
I’ll follow a simple directory structure for the app. Here are the important files:
我将为应用程序遵循一个简单的目录结构。 以下是重要文件:
src
├── App.css // App styles
├── App.js // App container
├── components
│ ├── Header.js // Header component
│ ├── Results.js // Results component
│ ├── SearchFilters.js // Filters component
│ └── Topic.js // rendered by Results
├── index.css // styles
├── index.js // ReactDOM render
└── theme.js // colors and fonts
public
└── index.html
Here’s the link to final repo if you wish to reference anything at any point.
如果您想随时参考任何内容,请访问最终仓库 。
I’ve written responsive styles for the app which you can copy into your app. Just fire up your favorite text editor and copy the styles for /src/index.css
from here and /src/App.css
from here respectively.
我已经为应用程序编写了响应样式,您可以将其复制到应用程序中。 刚刚火起来的文本编辑器和复制的样式/src/index.css
从这里和/src/App.css
从这里分别。
Now, create a file /src/theme.js
where we’ll add the colors and fonts for our app:
现在,创建一个文件/src/theme.js
,在其中添加应用程序的颜色和字体:
All the ReactiveSearch components are wrapped around a container component ReactiveBase which provides data from Elasticsearch to the children ReactiveSearch components.
所有ReactiveSearch组件都包装在一个容器组件ReactiveBase周围,该组件将Elasticsearch中的数据提供给子级ReactiveSearch组件。
We’ll use this in /src/App.js
:
我们将在/src/App.js
使用它:
import React, { Component } from 'react';
import { ReactiveBase } from '@appbaseio/reactivesearch';
import theme from './theme';
import './App.css';
class App extends Component {
render() {
return (
<section className="container">
<ReactiveBase
app="gitxplore-app"
credentials="4oaS4Srzi:f6966181-1eb4-443c-8e0e-b7f38e7bc316"
type="gitxplore-latest"
theme={theme}
>
<nav className="navbar">
<div className="title">GitXplore</div>
</nav>
</ReactiveBase>
</section>
);
}
}
export default App;
For the app
and credentials
prop you may use the ones I’ve provided here as it is. If you cloned the dataset in your own app earlier you can get them from the app’s credentials page. If you’re already familiar with Elasticsearch you may instead pass a url
prop referring to your own Elasticsearch cluster URL.
对于app
和credentials
道具,您可以按原样使用我在此处提供的内容。 如果您早先在自己的应用程序中克隆了数据集,则可以从应用程序的凭据页面获取它们。 如果您已经熟悉Elasticsearch,则可以传递一个引用您自己的Elasticsearch集群URL的url
属性 。
Alternatively, you can also copy your app’s credentials
from the apps dashboard. Hover over your app’s card and click on Copy Read Credentials.
或者,您也可以从应用程序仪表板复制应用程序的credentials
。 将鼠标悬停在您的应用卡上,然后点击复制已读凭据 。
After adding this you would see a basic layout like this:
添加后,您将看到如下基本布局:
Next, I’ll be adding a DataSearch component to search through repositories. It creates a search UI component and lets us search across one or more fields easily. The updated render
function in /src/App.js
would look like this:
接下来,我将添加一个DataSearch组件以搜索存储库。 它创建了一个搜索UI组件,使我们可以轻松地跨一个或多个字段进行搜索。 /src/App.js
更新的render
函数如下所示:
// importing DataSearch here
import { ReactiveBase, DataSearch } from '@appbaseio/reactivesearch';
...
<ReactiveBase ... >
// Adding the DataSearch here
<div className="flex row-reverse app-container">
<div className="results-container">
<DataSearch
componentId="repo"
filterLabel="Search"
dataField={['name', 'description', 'name.raw', 'fullname', 'owner', 'topics']}
placeholder="Search Repos"
autosuggest={false}
iconPosition="left"
URLParams
className="data-search-container results-container"
innerClass={{
input: 'search-input',
}}
/>
</div>
</div>
</ReactiveBase>
...
The DataSearch
component goes inside the ReactiveBase
component and receives all the necessary data from it so we don’t have to write Elasticsearch queries ourselves. The surrounding div
s add some className
properties for styling. These just add a layout to the app. You can go through all the styles at /src/App.css
which we created earlier. You might have noticed that we have passed some props to the DataSearch
component.
DataSearch
组件位于ReactiveBase
组件内部,并从中接收所有必要的数据,因此我们不必自己编写Elasticsearch查询。 周围的div
添加了一些className
属性以进行样式设置。 这些只是将布局添加到应用程序。 您可以在我们之前创建的/src/App.css
浏览所有样式。 您可能已经注意到我们已经将一些道具传递给了DataSearch
组件。
Here’s how they work:
它们的工作方式如下:
componentId
: a unique string identifier which we’ll use later to connect two different ReactiveSearch components.
componentId
:一个唯一的字符串标识符,我们稍后将使用它来连接两个不同的ReactiveSearch组件。
filterLabel
: a string value which will show up in the filters menu later.
filterLabel
:一个字符串值,稍后将在过滤器菜单中显示。
dataField
: an array of strings containing Elasticsearch fields on which search has to performed on. You can check the dataset and see that these fields also matches the column name. All fields specified here matches the structure of data, for example name
refers to the name of repo, description
refers to its description, but there is a field with a .raw
added here, name.raw
which is a multi-field of the name
field. Elasticsearch can index the same data in different ways for different purposes, which we can use to get better search results.
dataField
:一个字符串数组,其中包含必须对其执行搜索的Elasticsearch字段。 您可以检查数据集,并查看这些字段是否也与列名匹配。 此处指定的所有字段的数据的结构,例如火柴name
是指回购的名称, description
指的是其描述,但存在与场.raw
此处添加, name.raw
这是一个多字段的的name
领域。 Elasticsearch可以出于不同目的以不同方式为相同数据建立索引,我们可以使用它来获得更好的搜索结果。
placeholder
: sets the placeholder value in the input box.
placeholder
:在输入框中设置占位符值。
autosuggest
: setting a false
value for the prop causes the results to update immediately in the results.
autosuggest
:为prop设置一个false
值会使结果立即在结果中更新。
iconPosition
: sets the position of the ? icon.
iconPosition
:设置位置? 图标。
URLParams
: is a boolean
which tells the component to save the search term in the browser’s URL so we can share a URL to a specific search query. For example, check this link to see all results related to “react”.
URLParams
:是一个boolean
,它告诉组件将搜索词保存在浏览器的URL中,以便我们可以将URL共享给特定的搜索查询。 例如,检查此链接以查看与“React”相关的所有结果。
className
: adds a class
for styling using CSS.
className
:添加一个用于使用CSS进行样式设置的class
。
innerClass
: adds a class
to different sections of a component for styling using CSS. Here, I’ve added a class
to the input
box for styling. A detailed description can be found in the docs.
innerClass
:向组件的不同部分添加一个class
以使用CSS进行样式设置。 在这里,我将一个class
添加到input
框以进行样式设置。 详细说明可以在docs中找到。
With this, our app should get a working search bar:
有了这个,我们的应用程序应该会得到一个正常的搜索栏:
Next, we’ll be adding the Results
component at /src/components/Results.js
and importing it in /src/App.js
.
接下来,我们将在/src/components/Results.js
添加Results
组件,并将其导入/src/App.js
。
Here’s how you can write the Results
component:
这是编写“ Results
组件的方法:
import React from 'react';
import { SelectedFilters, ReactiveList } from '@appbaseio/reactivesearch';
const onResultStats = (results, time) => (
<div className="flex justify-end">
{results} results found in {time}ms
</div>
);
const onData = (data) => (
<div className="result-item" key={data.fullname}>
{data.owner}/{data.name}
</div>
);
const Results = () => (
<div className="result-list">
<SelectedFilters className="m1" />
<ReactiveList
componentId="results"
dataField="name"
onData={onData}
onResultStats={onResultStats}
react={{
and: ['repo'],
}}
pagination
innerClass={{
list: 'result-list-container',
pagination: 'result-list-pagination',
resultsInfo: 'result-list-info',
poweredBy: 'powered-by',
}}
size={6}
/>
</div>
);
export default Results;
I’ve imported two new components from ReactiveSearch, SelectedFilters
and ReactiveList
. SelectedFilters will render the filters for our ReactiveSearch components at one place:
我从ReactiveSearch导入了两个新组件, SelectedFilters
和ReactiveList
。 SelectedFilters将为我们的ReactiveSearch组件提供一个过滤器:
ReactiveList renders the search results. Here’s how its props work:
ReactiveList呈现搜索结果。 其道具的工作原理如下:
dataField
: orders the results using name
field here.
dataField
:在此处使用name
字段对结果进行dataField
。
onData
: accepts a function which returns a JSX. The function is passed each result individually. Here we’re generating a basic UI which we’ll modify later.
onData
:接受一个返回JSX的函数。 该函数分别传递每个结果。 在这里,我们生成了一个基本的UI,稍后将对其进行修改。
onResultStats
: similar to onData
but for the result stats. The function is passed the number of results
found and time
taken.
onResultStats
:类似于onData
但用于结果统计。 该函数将传递找到的results
数和time
。
react
: the react
prop tells the ReactiveList
to listen to changes made byCategorySearch
component, we’ve provided the componentId
of the CategorySearch
component here called repo
. Later we’ll add more components here.
react
:该react
道具告诉ReactiveList
听由所做的更改CategorySearch
组件,我们提供的componentId
中的CategorySearch
这里称为组件repo
。 稍后,我们将在此处添加更多组件。
pagination
: a boolean
which tells the ReactiveList to split the results into pages, each page containing the number of results specified in the size
prop.
pagination
:一个boolean
,它告诉ReactiveList将结果分成页面,每个页面包含size
属性中指定的结果数。
Now we can import
and use the Results
component in /src/App.js
. Just add it inside the div
with results-container
class.
现在,我们可以在/src/App.js
import
并使用Results
组件。 只需将它添加到带有results-container
类的div
。
...
import Results from './components/Results';
...
render() {
return(
...
<div className="results-container">
<DataSearch ... />
<Results />
</div>
...
)
}
With this component, a basic version of our search UI should start coming together:
有了这个组件,我们的搜索UI的基本版本应该开始组合在一起:
Lets create a Header
component at /src/components/Header.js
which we’ll use to render more search filters.
让我们在/src/components/Header.js
创建一个Header
组件,该组件将用于呈现更多搜索过滤器。
Here’s how to create a simple Header
component:
以下是创建简单的Header
组件的方法:
import React, { Component } from 'react';
import SearchFilters from './SearchFilters';
class Header extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
};
}
toggleVisibility = () => {
const visible = !this.state.visible;
this.setState({
visible,
});
}
render() {
return (
<nav className={`navbar ${this.state.visible ? 'active' : ''}`}>
<div className="title">GitXplore</div>
<div className="btn toggle-btn" onClick={this.toggleVisibility}>Toggle Filters</div>
<SearchFilters {...this.props} visible={this.state.visible} />
</nav>
);
}
}
export default Header;
I’ve moved the navigation code in <nav>..</nav>
from /src/App.js
here. The Header component has a method which toggles visible in the state. We’re using this to add a class which would make it take up the entire screen size on mobile layout. I’ve also added a toggle button which calls the toggleVisibility
method.
我已将导航代码从/src/App.js
移至<nav>..</nav>
。 Header组件具有在状态中切换可见状态的方法。 我们正在使用它来添加一个类,该类将使其占据移动版式上的整个屏幕尺寸。 我还添加了一个切换按钮,该按钮调用toggleVisibility
方法。
It also renders another component called SearchFilters
and passes all the props from the parent App
component. Let’s create this component to see things in action.
它还呈现另一个名为SearchFilters
组件,并传递来自父App
组件的所有道具。 让我们创建此组件以查看实际情况。
Create a new file /src/components/SearchFilters.js
:
创建一个新文件/src/components/SearchFilters.js
:
import React from 'react';
const SearchFilters = () => (
<div>
Search filters go here!
</div>
);
export default SearchFilters;
Next, I’ll update the App
component to use the Header
component that we created just now.
接下来,我将更新App
组件以使用我们刚才创建的Header
组件。
We’ll add a state
variable in App
component called currentTopics
which would be an array of currently selected topics in the app.
我们将在名为currentTopics
App
组件中添加一个state
变量,该变量将是应用程序中当前选定主题的数组。
We’ll then use the currentTopics
and pass them to the Header
and Results
components:
然后,我们将使用currentTopics
并将它们传递给Header
和Results
组件:
The setTopics
method will set whichever topics are passed to it, which we’ll pass to the Header
component. The toggleTopic
method will remove a topic from the state
in currentTopics
if it’s already present and add the topic if it is not present.
setTopics
方法将设置传递给它的主题,然后将其传递给Header
组件。 toggleTopic
方法将在currentTopics
中的某个state
中删除该主题(如果已经存在),并添加该主题(如果不存在)。
We’ll pass the toggleTopic
method to the Results
component:
我们将把toggleTopic
方法传递给Results
组件:
Lets add more filters to the UI in /src/components/SearchFilters.js
. I’ll be using three new components from ReactiveSearch here, MultiDropdownList
, SingleDropdownRange
and RangeSlider
. The components are used in a similar fashion as we used the DataSearch
component earlier.
让我们在/src/components/SearchFilters.js
向UI添加更多过滤器。 我将在这里使用来自ReactiveSearch的三个新组件: MultiDropdownList
, SingleDropdownRange
和RangeSlider
。 这些组件的使用方式与我们之前使用DataSearch
组件的方式类似。
Here’s the code:
这是代码:
import React from 'react';
import PropTypes from 'prop-types';
import {
MultiDropdownList,
SingleDropdownRange,
RangeSlider,
} from '@appbaseio/reactivesearch';
const SearchFilters = ({ currentTopics, setTopics, visible }) => (
<div className={`flex column filters-container ${!visible ? 'hidden' : ''}`}>
<div className="child m10">
<MultiDropdownList
componentId="language"
dataField="language.raw"
placeholder="Select languages"
title="Language"
filterLabel="Language"
/>
</div>
<div className="child m10">
<MultiDropdownList
componentId="topics"
dataField="topics.raw"
placeholder="Select topics"
title="Repo Topics"
filterLabel="Topics"
size={1000}
queryFormat="and"
defaultSelected={currentTopics}
onValueChange={setTopics}
/>
</div>
<div className="child m10">
<SingleDropdownRange
componentId="pushed"
dataField="pushed"
placeholder="Repo last active"
title="Last Active"
filterLabel="Last Active"
data={[
{ start: 'now-1M', end: 'now', label: 'Last 30 days' },
{ start: 'now-6M', end: 'now', label: 'Last 6 months' },
{ start: 'now-1y', end: 'now', label: 'Last year' },
]}
/>
</div>
<div className="child m10">
<SingleDropdownRange
componentId="created"
dataField="created"
placeholder="Repo created"
title="Created"
filterLabel="Created"
data={[
{
start: '2017-01-01T00:00:00Z',
end: '2017-12-31T23:59:59Z',
label: '2017',
},
{
start: '2016-01-01T00:00:00Z',
end: '2016-12-31T23:59:59Z',
label: '2016',
},
{
start: '2015-01-01T00:00:00Z',
end: '2015-12-31T23:59:59Z',
label: '2015',
},
{
start: '2014-01-01T00:00:00Z',
end: '2014-12-31T23:59:59Z',
label: '2014',
},
{
start: '2013-01-01T00:00:00Z',
end: '2013-12-31T23:59:59Z',
label: '2013',
},
{
start: '2012-01-01T00:00:00Z',
end: '2012-12-31T23:59:59Z',
label: '2012',
},
{
start: '2011-01-01T00:00:00Z',
end: '2011-12-31T23:59:59Z',
label: '2011',
},
{
start: '2010-01-01T00:00:00Z',
end: '2010-12-31T23:59:59Z',
label: '2010',
},
{
start: '2009-01-01T00:00:00Z',
end: '2009-12-31T23:59:59Z',
label: '2009',
},
{
start: '2008-01-01T00:00:00Z',
end: '2008-12-31T23:59:59Z',
label: '2008',
},
{
start: '2007-01-01T00:00:00Z',
end: '2007-12-31T23:59:59Z',
label: '2007',
},
]}
/>
</div>
<div className="child m10">
<RangeSlider
componentId="stars"
title="Repo Stars"
dataField="stars"
range={{ start: 0, end: 300000 }}
showHistogram={false}
rangeLabels={{
start: '0 Stars',
end: '300K Stars',
}}
innerClass={{
label: 'range-label',
}}
/>
</div>
<div className="child m10">
<RangeSlider
componentId="forks"
title="Repo Forks"
dataField="forks"
range={{ start: 0, end: 180500 }}
showHistogram={false}
rangeLabels={{
start: '0 Forks',
end: '180K Forks',
}}
innerClass={{
label: 'range-label',
}}
/>
</div>
</div>
);
SearchFilters.propTypes = {
currentTopics: PropTypes.arrayOf(PropTypes.string),
setTopics: PropTypes.func,
visible: PropTypes.bool,
};
export default SearchFilters;
The SearchFilters
component we’ve created above takes in three props from the Header
component, currentTopics
, setTopics
and visible
. The visible
prop is just used to add a className
for styling.
我们上面创建的SearchFilters
组件从Header
组件中获取了三个道具, currentTopics
, setTopics
和visible
。 visible
道具仅用于添加className
以进行样式设置。
The first component we’ve used here is a MultiDropdownList
which renders a dropdown component to select multiple options. The first MultiDropdownList
has a dataField
of language.raw
. It’ll populate itself with all the languages available in the repositories dataset.
我们在这里使用的第一个组件是MultiDropdownList
,它呈现一个下拉组件以选择多个选项。 第一个MultiDropdownList
具有language.raw
的dataField
。 它将使用存储库数据集中的所有可用语言填充自身。
We’ve used another MultiDropdownList
to render a list of topics:
我们使用了另一个MultiDropdownList
来呈现主题列表:
<MultiDropdownList
componentId="topics"
dataField="topics.raw"
placeholder="Select languages"
title="Repo Topics"
filterLabel="Topics"
size={1000}
queryFormat="and"
defaultSelected={currentTopics}
onValueChange={setTopics}
/>
Here’s how the props work here:
道具在这里的工作方式如下:
componentId
: similar to the previous ReactiveSearch components, this is a unique identifier which we’ll later associate in the Results
component that we created to get search results.
componentId
:类似于先前的ReactiveSearch组件,这是一个唯一的标识符,我们稍后将其与我们创建的Results
组件相关联以获取搜索结果。
dataField
: maps the component to the topics.raw
field in Elasticsearch.
dataField
:将组件映射到Elasticsearch中的topics.raw
字段。
placeholder
: sets the placeholder value when nothing is selected.
placeholder
:在未选择任何内容时设置占位符值。
title
: adds a title for the component in the UI.
title
:在UI中为组件添加标题。
filterLabel
: sets the label of the components in the removable filters (the SelectedFilters
which we used in the Results
component).
filterLabel
:设置可移动过滤器(在“ Results
组件中使用的SelectedFilters
组件的标签。
size
: tells the component to render a maximum of 1000
items in the list.
size
:告诉组件在列表中最多渲染1000
项目。
queryFormat
: when set to 'and'
as we’ve used here, it gives results which matches all the selected tags (exactly like intersection).
queryFormat
:如我们在此处使用'and'
那样设置为'and'
,它给出的结果与所有选定标签匹配(完全像intersection )。
defaultSelected
: sets the selected items in the component. Here we’re passing currentTopics
which we’ve stored in the state
at /src/App.js
.
defaultSelected
:设置组件中的选定项目。 在这里,我们传递了currentTopics
,该state
已存储在state
为/src/App.js
。
onValueChange
: is a function that will be called by the component when we make a change in its value. Here we call the setTopics
function which we received in the props. Therefore, whenever we select or deselect a value in the component it would update the currentTopics
in the state
of main App
component.
onValueChange
:是一个函数,当我们更改其值时,它将由组件调用。 在这里,我们将调用道具中收到的setTopics
函数。 因此,无论何时在组件中选择或取消选择一个值,都会在App
主组件state
下更新currentTopics
。
The next ReactiveSearch component we’ve used here is a SingleDropdownRange
. It uses a new prop called data
.
我们在这里使用的下一个ReactiveSearch组件是SingleDropdownRange
。 它使用了一个称为data
的新道具。
Here’s how it works:
运作方式如下:
<SingleDropdownRange
...
data={[
{ start: 'now-1M', end: 'now', label: 'Last 30 days' },
{ start: 'now-6M', end: 'now', label: 'Last 6 months' },
{ start: 'now-1y', end: 'now', label: 'Last year' },
]}
/>
The data
prop accepts an array of objects with start
and end
values and shows the specified label
in the dropdown. It’s mapped to the pushed
field in the dataset which is a date type in Elasticsearch. One cool way to specify date range in Elasticsearch is using the now
keyword. now
refers to the current time, now-1M
refers to one month before, now-6M
to six month before and now-1y
to a year before now
.
data
道具接受带有start
和end
值的对象数组,并在下拉列表中显示指定的label
。 它被映射到数据集中的pushed
字段,该字段是Elasticsearch中的日期类型 。 在Elasticsearch中指定日期范围的一种很酷的方法是使用now
关键字。 now
指的是当前的时间, now-1M
指的是前一个月, now-6M
到六月份前和now-1y
一年之前, now
。
I’ve used another SingleDropdownRange
component for the created
field in the dataset.
我为数据集中的created
字段使用了另一个SingleDropdownRange
组件。
Here I’ve specified year ranges in datetime for different years:
在这里,我指定了不同年份的日期时间范围:
<SingleDropdownRange
...
data={[
{
start: '2017-01-01T00:00:00Z',
end: '2017-12-31T23:59:59Z',
label: '2017',
},
{
start: '2016-01-01T00:00:00Z',
end: '2016-12-31T23:59:59Z',
label: '2016',
},
...
]}
/>
The third component I’ve used is a RangeSlider
which renders a slider UI. I’ve used to RangeSlider
components, one for the stars
field and the other for forks
.
我使用的第三个组件是RangeSlider
,它呈现滑块UI。 我已经习惯了RangeSlider
组件,其中一个用于stars
字段,另一个用于forks
。
Two main props that this component introduces are range
and rangeLabels
:
此组件引入的两个主要rangeLabels
是range
和rangeLabels
:
<RangeSlider
...
showHistogram={false}
range={{ start: 0, end: 300000 }}
rangeLabels={{
start: '0 Stars',
end: '300K Stars',
}}
/>
range
: prop specifies a range for the data with a start
and end
value.
range
:prop用start
和end
值指定数据的范围。
rangeLabels
: prop takes the labels to show below the slider.
rangeLabels
:prop将标签显示在滑块下方。
showHistogram
: is a boolean
prop which shows a histogram with the distribution of data. Here I’ve set it to false
since it’s not needed.
showHistogram
:是一个boolean
道具,它显示带有数据分布的直方图。 在这里,由于不需要它,所以将其设置为false
。
Now we just need to connect these filters to the Results
component. We just have to update one line in the ReactiveList
rendered by the Results
component to include the componentId
s of these components.
现在,我们只需要将这些过滤器连接到“ Results
组件即可。 我们只需要更新的一条线ReactiveList
通过渲染Results
组件包括componentId
这些组件秒。
Update the react
prop in the ReactiveList
that we rendered in the Results
component:
更新我们在“ Results
组件中呈现的ReactiveList
中的react
prop:
const Results = () => (
<div className="result-list">
<SelectedFilters className="m1" />
<ReactiveList
... // updating the react prop here
react={{
and: ['language', 'topics', 'pushed', 'created', 'stars', 'forks', 'repo'],
}}
/>
</div>
);
That should make your results update for all the filters ?
那应该使所有过滤器的结果都更新吗?
Up until now, we’ve been seeing only a basic version of the results. As the final piece of this app, lets add some flair to the results ✌️
到目前为止,我们只看到了结果的基本版本。 作为此应用程序的最后一块,让我们为结果添加一些天赋✌️
We’ll be using another component inside our Results
components to render different topics.
我们将在“ Results
组件内使用另一个组件来呈现不同的主题。
Here’s how you can create your own at /src/components/Topic
. Feel free to add your own taste ?
您可以在/src/components/Topic
创建自己的方法。 随意添加自己的口味?
This component renders its children
and adds a click handler to toggle the topics which updates the currentTopics
inside the main App
component’s state.
该组件呈现其children
并添加单击处理程序以切换主题,从而更新主App
组件状态内的currentTopics
。
Next, we just need to update our Results
component at /src/components/Results.js
:
接下来,我们只需要在/src/components/Results.js
更新Results
组件:
I’ve updated the onData
function to render more detailed results. You’ll also notice a new sortOptions
prop in the ReactiveList
. This prop accepts an array of objects which renders a dropdown menu to select how you wish to sort the results. Each object contains a label
to display as the list item, a dataField
to sort the results on and a sortBy
key which can either be asc
(ascending) or desc
(descending).
我已经更新了onData
函数以呈现更详细的结果。 您还会在ReactiveList
注意到一个新的sortOptions
道具。 该道具接受一个对象数组,该对象呈现一个下拉菜单以选择您希望对结果进行排序的方式。 每个对象都包含一个要显示为列表项的label
,一个用于对结果进行排序的dataField
以及一个sortBy
键,该键可以是asc
(升序)或desc
(降序)。
That’s it, your very own GitHub repository explorer should be live!
就是这样,您自己的GitHub存储库浏览器应该已经上线了!
GitXplore app demo, CodeSandbox and source code
GitXplore应用程序演示 , CodeSandbox和源代码
ReactiveSearch docs
ReactiveSearch 文档
Hope you enjoyed this story. If you have any thoughts or suggestions, please let me know and do share your version of the app in comments!
希望您喜欢这个故事。 如果您有任何想法或建议,请告诉我,并在评论中分享您的应用版本!
You may follow me on twitter for latest updates. I've also started posting more recent posts on my personal blog.
您可以在Twitter上关注我以获取最新更新。 我还开始在我的个人博客上发布更多最新帖子。